From: Vsevolod Stakhov Date: Tue, 22 Sep 2015 17:17:24 +0000 (+0100) Subject: Rename main.h and main.c to `rspamd.X` X-Git-Tag: 1.0.2~12 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3afc4aba24dd996950c3947b9cb6475271aec884;p=rspamd.git Rename main.h and main.c to `rspamd.X` --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 494f5b6c0..f86c45b40 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ MACRO(_AddModulesForced MLIST) SET(MODULES_ID ${_MODULES_ID} CACHE INTERNAL "Modules ID" FORCE) SET(MODULES_C "${CMAKE_CURRENT_BINARY_DIR}/modules.c") FILE(WRITE "${MODULES_C}" - "/* ${MODULES_ID} */\n#include \"main.h\"\n") + "/* ${MODULES_ID} */\n#include \"rspamd.h\"\n") # Handle even old cmake LIST(LENGTH ${MLIST} MLIST_COUNT) @@ -28,7 +28,7 @@ ENDMACRO(_AddModulesForced MLIST) MACRO(_AddWorkersForced WLIST) SET(WORKERS_C "${CMAKE_CURRENT_BINARY_DIR}/workers.c") FILE(WRITE "${WORKERS_C}" - "#include \"main.h\"\n") + "#include \"rspamd.h\"\n") # Handle even old cmake LIST(LENGTH ${WLIST} WLIST_COUNT) @@ -74,7 +74,7 @@ ADD_SUBDIRECTORY(client) SET(RSPAMDSRC controller.c fuzzy_storage.c lua_worker.c - main.c + rspamd.c smtp_proxy.c worker.c http_proxy.c) diff --git a/src/controller.c b/src/controller.c index 0e729b886..69132de70 100644 --- a/src/controller.c +++ b/src/controller.c @@ -27,7 +27,7 @@ #include "libutil/rrd.h" #include "libutil/map.h" #include "libstat/stat_api.h" -#include "main.h" +#include "rspamd.h" #include "libserver/worker_util.h" #include "utlist.h" diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c index d41b922d2..ebbff3365 100644 --- a/src/fuzzy_storage.c +++ b/src/fuzzy_storage.c @@ -28,7 +28,7 @@ #include "config.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "protocol.h" #include "upstream.h" #include "cfg_file.h" diff --git a/src/fuzzy_storage.h b/src/fuzzy_storage.h index b9997da8b..57d7b5cc7 100644 --- a/src/fuzzy_storage.h +++ b/src/fuzzy_storage.h @@ -2,7 +2,7 @@ #define RSPAMD_FUZZY_STORAGE_H #include "config.h" -#include "main.h" +#include "rspamd.h" #include "shingles.h" #define RSPAMD_FUZZY_VERSION 3 diff --git a/src/http_proxy.c b/src/http_proxy.c index 12b237346..f71adc3f6 100644 --- a/src/http_proxy.c +++ b/src/http_proxy.c @@ -31,7 +31,7 @@ #include "libserver/url.h" #include "libserver/dns.h" #include "libmime/message.h" -#include "main.h" +#include "rspamd.h" #include "libserver/worker_util.h" #include "keypairs_cache.h" #include "ottery.h" diff --git a/src/libmime/filter.c b/src/libmime/filter.c index 7efb8f82a..ed50efafe 100644 --- a/src/libmime/filter.c +++ b/src/libmime/filter.c @@ -25,7 +25,7 @@ #include "config.h" #include "mem_pool.h" #include "filter.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "cfg_file.h" #include "util.h" diff --git a/src/libmime/images.c b/src/libmime/images.c index 419b6a2ca..f55e47d7a 100644 --- a/src/libmime/images.c +++ b/src/libmime/images.c @@ -23,7 +23,7 @@ #include "config.h" #include "images.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "html.h" diff --git a/src/libmime/images.h b/src/libmime/images.h index 5d01e6f75..6d64ea5d1 100644 --- a/src/libmime/images.h +++ b/src/libmime/images.h @@ -2,7 +2,7 @@ #define IMAGES_H_ #include "config.h" -#include "main.h" +#include "rspamd.h" struct html_image; diff --git a/src/libmime/message.c b/src/libmime/message.c index 54c5ab59e..3537f70c0 100644 --- a/src/libmime/message.c +++ b/src/libmime/message.c @@ -24,7 +24,7 @@ #include "config.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "cfg_file.h" #include "html.h" diff --git a/src/libmime/mime_expressions.c b/src/libmime/mime_expressions.c index f5732c75a..6581b7362 100644 --- a/src/libmime/mime_expressions.c +++ b/src/libmime/mime_expressions.c @@ -25,7 +25,7 @@ #include "config.h" #include "util.h" #include "cfg_file.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "mime_expressions.h" #include "html.h" diff --git a/src/libmime/smtp_proto.c b/src/libmime/smtp_proto.c index 713efabac..988bd37af 100644 --- a/src/libmime/smtp_proto.c +++ b/src/libmime/smtp_proto.c @@ -23,7 +23,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "cfg_file.h" #include "util.h" #include "smtp.h" diff --git a/src/libmime/smtp_utils.c b/src/libmime/smtp_utils.c index b3abe0302..edc7f3b14 100644 --- a/src/libmime/smtp_utils.c +++ b/src/libmime/smtp_utils.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "filter.h" #include "smtp.h" #include "smtp_proto.h" diff --git a/src/libserver/buffer.c b/src/libserver/buffer.c index 4c6ee2776..e4268863c 100644 --- a/src/libserver/buffer.c +++ b/src/libserver/buffer.c @@ -24,7 +24,7 @@ #include "config.h" #include "buffer.h" -#include "main.h" +#include "rspamd.h" #ifdef HAVE_SYS_SENDFILE_H #include #endif diff --git a/src/libserver/cfg_rcl.c b/src/libserver/cfg_rcl.c index 263ba9e40..efd57f715 100644 --- a/src/libserver/cfg_rcl.c +++ b/src/libserver/cfg_rcl.c @@ -23,7 +23,7 @@ #include #include "cfg_rcl.h" -#include "main.h" +#include "rspamd.h" #include "uthash_strcase.h" #include "utlist.h" #include "cfg_file.h" diff --git a/src/libserver/cfg_utils.c b/src/libserver/cfg_utils.c index 068ced265..95c984b9c 100644 --- a/src/libserver/cfg_utils.c +++ b/src/libserver/cfg_utils.c @@ -26,7 +26,7 @@ #include "config.h" #include "cfg_file.h" -#include "main.h" +#include "rspamd.h" #include "uthash_strcase.h" #include "filter.h" #include "lua/lua_common.h" diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c index dd4242393..2cfa184a3 100644 --- a/src/libserver/dkim.c +++ b/src/libserver/dkim.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "dkim.h" #include "dns.h" diff --git a/src/libserver/dns.c b/src/libserver/dns.c index f808a4460..0df6ddd91 100644 --- a/src/libserver/dns.c +++ b/src/libserver/dns.c @@ -25,7 +25,7 @@ #include "config.h" #include "dns.h" -#include "main.h" +#include "rspamd.h" #include "utlist.h" #include "uthash.h" #include "rdns_event.h" diff --git a/src/libserver/dynamic_cfg.c b/src/libserver/dynamic_cfg.c index 97a9d72f2..eaa658b8b 100644 --- a/src/libserver/dynamic_cfg.c +++ b/src/libserver/dynamic_cfg.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "map.h" #include "filter.h" #include "dynamic_cfg.h" diff --git a/src/libserver/events.c b/src/libserver/events.c index a50c122f3..b996f1559 100644 --- a/src/libserver/events.c +++ b/src/libserver/events.c @@ -23,7 +23,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "events.h" #include "xxhash.h" diff --git a/src/libserver/fuzzy_backend.c b/src/libserver/fuzzy_backend.c index 3d1749aa7..ce6bd6a5a 100644 --- a/src/libserver/fuzzy_backend.c +++ b/src/libserver/fuzzy_backend.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "fuzzy_backend.h" #include diff --git a/src/libserver/html.c b/src/libserver/html.c index e68c73c8c..0a46cef02 100644 --- a/src/libserver/html.c +++ b/src/libserver/html.c @@ -24,7 +24,7 @@ #include "config.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "html.h" #include "url.h" diff --git a/src/libserver/protocol.c b/src/libserver/protocol.c index d585f9ce6..8cf6b771f 100644 --- a/src/libserver/protocol.c +++ b/src/libserver/protocol.c @@ -23,7 +23,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "util.h" #include "cfg_file.h" #include "cfg_rcl.h" diff --git a/src/libserver/proxy.c b/src/libserver/proxy.c index a4076954b..09171eeb9 100644 --- a/src/libserver/proxy.c +++ b/src/libserver/proxy.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "proxy.h" static void rspamd_proxy_backend_handler (gint fd, gshort what, gpointer data); diff --git a/src/libserver/roll_history.c b/src/libserver/roll_history.c index 52049cb5e..74c7c8214 100644 --- a/src/libserver/roll_history.c +++ b/src/libserver/roll_history.c @@ -24,7 +24,7 @@ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "roll_history.h" static const gchar rspamd_history_magic[] = {'r', 's', 'h', '1'}; diff --git a/src/libserver/spf.c b/src/libserver/spf.c index 479f22e93..d9b9a730f 100644 --- a/src/libserver/spf.c +++ b/src/libserver/spf.c @@ -25,7 +25,7 @@ #include "config.h" #include "dns.h" #include "spf.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "filter.h" #include "utlist.h" diff --git a/src/libserver/symbols_cache.c b/src/libserver/symbols_cache.c index 52b62fac5..6564d9efe 100644 --- a/src/libserver/symbols_cache.c +++ b/src/libserver/symbols_cache.c @@ -24,7 +24,7 @@ #include "config.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "symbols_cache.h" #include "cfg_file.h" diff --git a/src/libserver/task.c b/src/libserver/task.c index ce765b8f0..fc22f784f 100644 --- a/src/libserver/task.c +++ b/src/libserver/task.c @@ -22,7 +22,7 @@ */ #include "task.h" -#include "main.h" +#include "rspamd.h" #include "filter.h" #include "protocol.h" #include "message.h" diff --git a/src/libserver/url.c b/src/libserver/url.c index 3a99480cd..b2e5d691c 100644 --- a/src/libserver/url.c +++ b/src/libserver/url.c @@ -29,7 +29,7 @@ #include "url.h" #include "util.h" #include "fstring.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "http.h" #include "acism.h" diff --git a/src/libserver/worker_util.c b/src/libserver/worker_util.c index 800fa9c01..eee200a41 100644 --- a/src/libserver/worker_util.c +++ b/src/libserver/worker_util.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "message.h" #include "lua/lua_common.h" #include "worker_util.h" diff --git a/src/libserver/worker_util.h b/src/libserver/worker_util.h index 04109b9b0..8bc0ceaee 100644 --- a/src/libserver/worker_util.h +++ b/src/libserver/worker_util.h @@ -26,7 +26,7 @@ #include "config.h" #include "util.h" #include "http.h" -#include "main.h" +#include "rspamd.h" #ifndef HAVE_SA_SIGINFO typedef void (*rspamd_sig_handler_t) (gint); diff --git a/src/libstat/backends/mmaped_file.c b/src/libstat/backends/mmaped_file.c index 6bb5e0feb..41d6900ce 100644 --- a/src/libstat/backends/mmaped_file.c +++ b/src/libstat/backends/mmaped_file.c @@ -24,7 +24,7 @@ #include "config.h" #include "stat_internal.h" -#include "main.h" +#include "rspamd.h" #define CHAIN_LENGTH 128 diff --git a/src/libstat/backends/redis.c b/src/libstat/backends/redis.c index 38d6c7a3a..39d7dc21a 100644 --- a/src/libstat/backends/redis.c +++ b/src/libstat/backends/redis.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "stat_internal.h" #include "upstream.h" diff --git a/src/libstat/backends/sqlite3_backend.c b/src/libstat/backends/sqlite3_backend.c index 96eddea3e..33aac0494 100644 --- a/src/libstat/backends/sqlite3_backend.c +++ b/src/libstat/backends/sqlite3_backend.c @@ -23,7 +23,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "sqlite3.h" #include "libutil/sqlite_utils.h" #include "libstat/stat_internal.h" diff --git a/src/libstat/classifiers/bayes.c b/src/libstat/classifiers/bayes.c index e248fd6e6..67c2e3116 100644 --- a/src/libstat/classifiers/bayes.c +++ b/src/libstat/classifiers/bayes.c @@ -26,7 +26,7 @@ * Bayesian classifier */ #include "classifiers.h" -#include "main.h" +#include "rspamd.h" #include "filter.h" #include "cfg_file.h" #include "stat_internal.h" diff --git a/src/libstat/learn_cache/sqlite3_cache.c b/src/libstat/learn_cache/sqlite3_cache.c index 745b8839c..6eabaefda 100644 --- a/src/libstat/learn_cache/sqlite3_cache.c +++ b/src/libstat/learn_cache/sqlite3_cache.c @@ -23,7 +23,7 @@ #include "config.h" #include "learn_cache.h" -#include "main.h" +#include "rspamd.h" #include "stat_api.h" #include "stat_internal.h" #include "blake2.h" diff --git a/src/libstat/stat_config.c b/src/libstat/stat_config.c index 00baaa059..dbfe16c27 100644 --- a/src/libstat/stat_config.c +++ b/src/libstat/stat_config.c @@ -25,7 +25,7 @@ #include "config.h" #include "stat_api.h" -#include "main.h" +#include "rspamd.h" #include "cfg_rcl.h" #include "stat_internal.h" diff --git a/src/libstat/stat_process.c b/src/libstat/stat_process.c index d1e23baf2..a6f5d31ae 100644 --- a/src/libstat/stat_process.c +++ b/src/libstat/stat_process.c @@ -23,7 +23,7 @@ #include "config.h" #include "stat_api.h" -#include "main.h" +#include "rspamd.h" #include "stat_internal.h" #include "libmime/message.h" #include "libmime/images.h" diff --git a/src/libstat/tokenizers/tokenizers.c b/src/libstat/tokenizers/tokenizers.c index b54c89023..07e7a1f45 100644 --- a/src/libstat/tokenizers/tokenizers.c +++ b/src/libstat/tokenizers/tokenizers.c @@ -26,7 +26,7 @@ * Common tokenization functions */ -#include "main.h" +#include "rspamd.h" #include "tokenizers.h" #include "stat_internal.h" diff --git a/src/libstat/tokenizers/tokenizers.h b/src/libstat/tokenizers/tokenizers.h index 050f6d7b1..7b01d9fe8 100644 --- a/src/libstat/tokenizers/tokenizers.h +++ b/src/libstat/tokenizers/tokenizers.h @@ -4,7 +4,7 @@ #include "config.h" #include "mem_pool.h" #include "fstring.h" -#include "main.h" +#include "rspamd.h" #include "stat_api.h" #define RSPAMD_DEFAULT_TOKENIZER "osb" diff --git a/src/libutil/aio_event.c b/src/libutil/aio_event.c index ed257bab0..0686bbd0d 100644 --- a/src/libutil/aio_event.c +++ b/src/libutil/aio_event.c @@ -23,7 +23,7 @@ #include "config.h" #include "aio_event.h" -#include "main.h" +#include "rspamd.h" #ifdef HAVE_SYS_EVENTFD_H #include diff --git a/src/libutil/keypairs_cache.c b/src/libutil/keypairs_cache.c index 532eaa373..1bedb704d 100644 --- a/src/libutil/keypairs_cache.c +++ b/src/libutil/keypairs_cache.c @@ -24,7 +24,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "keypairs_cache.h" #include "keypair_private.h" #include "hash.h" diff --git a/src/libutil/logger.c b/src/libutil/logger.c index 57410ad96..65cb67cdc 100644 --- a/src/libutil/logger.c +++ b/src/libutil/logger.c @@ -26,7 +26,7 @@ #include "config.h" #include "logger.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "map.h" #include "xxhash.h" diff --git a/src/libutil/map.c b/src/libutil/map.c index 2ef68302e..cba82ba41 100644 --- a/src/libutil/map.c +++ b/src/libutil/map.c @@ -28,7 +28,7 @@ #include "config.h" #include "map.h" #include "http.h" -#include "main.h" +#include "rspamd.h" #include "util.h" #include "mem_pool.h" #include "blake2.h" diff --git a/src/libutil/mem_pool.c b/src/libutil/mem_pool.c index 8070e1c2b..ea5fa0bd8 100644 --- a/src/libutil/mem_pool.c +++ b/src/libutil/mem_pool.c @@ -27,7 +27,7 @@ #include "fstring.h" #include "logger.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "utlist.h" #include "ottery.h" diff --git a/src/libutil/printf.c b/src/libutil/printf.c index 03b801041..328173bd0 100644 --- a/src/libutil/printf.c +++ b/src/libutil/printf.c @@ -25,7 +25,7 @@ #include "printf.h" #include "fstring.h" -#include "main.h" +#include "rspamd.h" /** * From FreeBSD libutil code diff --git a/src/libutil/radix.c b/src/libutil/radix.c index a919691da..bea96b142 100644 --- a/src/libutil/radix.c +++ b/src/libutil/radix.c @@ -25,7 +25,7 @@ #include "config.h" #include "radix.h" -#include "main.h" +#include "rspamd.h" #include "mem_pool.h" #define msg_err_radix(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ diff --git a/src/libutil/regexp.c b/src/libutil/regexp.c index da9884ec1..f713ca2f7 100644 --- a/src/libutil/regexp.c +++ b/src/libutil/regexp.c @@ -28,7 +28,7 @@ #include "blake2.h" #include "ref.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include typedef guchar regexp_id_t[BLAKE2B_OUTBYTES]; diff --git a/src/libutil/util.c b/src/libutil/util.c index 983accf62..496bd0298 100644 --- a/src/libutil/util.c +++ b/src/libutil/util.c @@ -26,7 +26,7 @@ #include "config.h" #include "util.h" #include "cfg_file.h" -#include "main.h" +#include "rspamd.h" #include "filter.h" #include "message.h" diff --git a/src/lua/lua_common.h b/src/lua/lua_common.h index b2b2750b5..3aee6ea52 100644 --- a/src/lua/lua_common.h +++ b/src/lua/lua_common.h @@ -8,7 +8,7 @@ #include #include -#include "main.h" +#include "rspamd.h" #include "ucl.h" #include "lua_ucl.h" diff --git a/src/lua/lua_util.c b/src/lua/lua_util.c index f533434ce..f9f71c532 100644 --- a/src/lua/lua_util.c +++ b/src/lua/lua_util.c @@ -23,7 +23,7 @@ #include "lua_common.h" #include "task.h" -#include "main.h" +#include "rspamd.h" #include "html.h" #include "cfg_rcl.h" #include "tokenizers/tokenizers.h" diff --git a/src/lua_worker.c b/src/lua_worker.c index 8e6aaa8fc..c378ffc0b 100644 --- a/src/lua_worker.c +++ b/src/lua_worker.c @@ -24,7 +24,7 @@ #include "config.h" #include "util.h" -#include "main.h" +#include "rspamd.h" #include "libserver/worker_util.h" #include "protocol.h" #include "upstream.h" diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 87de76bee..000000000 --- a/src/main.c +++ /dev/null @@ -1,1420 +0,0 @@ -/* - * 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 "main.h" -#include "lmtp.h" -#include "smtp.h" -#include "libutil/map.h" -#include "fuzzy_storage.h" -#include "libserver/symbols_cache.h" -#include "lua/lua_common.h" -#include "libserver/worker_util.h" -#include "ottery.h" -#include "xxhash.h" -#include "utlist.h" -#include "libstat/stat_api.h" -#include "cryptobox.h" -#include "regexp.h" -#ifdef HAVE_OPENSSL -#include -#include -#include -#include -#include -#endif -#ifdef HAVE_LOCALE_H -#include -#define HAVE_SETLOCALE 1 -#endif - -#define msg_err_main(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ - rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ - G_STRFUNC, \ - __VA_ARGS__) -#define msg_warn_main(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ - rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ - G_STRFUNC, \ - __VA_ARGS__) -#define msg_info_main(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ - rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ - G_STRFUNC, \ - __VA_ARGS__) -#define msg_debug_main(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \ - rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ - G_STRFUNC, \ - __VA_ARGS__) - -/* 2 seconds to fork new process in place of dead one */ -#define SOFT_FORK_TIME 2 - -/* 10 seconds after getting termination signal to terminate all workers with SIGKILL */ -#define HARD_TERMINATION_TIME 10 - -static struct rspamd_worker * fork_worker (struct rspamd_main *, - struct rspamd_worker_conf *, guint); -static gboolean load_rspamd_config (struct rspamd_config *cfg, - gboolean init_modules); - -sig_atomic_t do_restart = 0; -sig_atomic_t do_reopen_log = 0; -sig_atomic_t do_terminate = 0; -sig_atomic_t child_dead = 0; -sig_atomic_t got_alarm = 0; - -#ifdef HAVE_SA_SIGINFO -GQueue *signals_info = NULL; -#endif - -static gboolean config_test = FALSE; -static gboolean no_fork = FALSE; -static gchar **cfg_names = NULL; -static gchar **lua_tests = NULL; -static gchar **sign_configs = NULL; -static gchar *privkey = NULL; -static gchar *rspamd_user = NULL; -static gchar *rspamd_group = NULL; -static gchar *rspamd_pidfile = NULL; -static gboolean dump_cache = FALSE; -static gboolean is_debug = FALSE; -static gboolean is_insecure = FALSE; -static gboolean gen_keypair = FALSE; -static gboolean encrypt_password = FALSE; -/* List of workers that are pending to start */ -static GList *workers_pending = NULL; -static GHashTable *vars = NULL; - -#ifdef HAVE_SA_SIGINFO -static siginfo_t static_sg[64]; -static sig_atomic_t cur_sg = 0; -#endif - -/* List of unrelated forked processes */ -static GArray *other_workers = NULL; - -/* List of active listen sockets indexed by worker type */ -static GHashTable *listen_sockets = NULL; - -struct rspamd_main *rspamd_main; - -/* Defined in modules.c */ -extern module_t *modules[]; -extern worker_t *workers[]; - -/* Commandline options */ -static GOptionEntry entries[] = -{ - { "config-test", 't', 0, G_OPTION_ARG_NONE, &config_test, - "Do config test and exit", NULL }, - { "no-fork", 'f', 0, G_OPTION_ARG_NONE, &no_fork, - "Do not daemonize main process", NULL }, - { "config", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &cfg_names, - "Specify config file(s)", NULL }, - { "user", 'u', 0, G_OPTION_ARG_STRING, &rspamd_user, - "User to run rspamd as", NULL }, - { "group", 'g', 0, G_OPTION_ARG_STRING, &rspamd_group, - "Group to run rspamd as", NULL }, - { "pid", 'p', 0, G_OPTION_ARG_STRING, &rspamd_pidfile, "Path to pidfile", - NULL }, - { "dump-cache", 'C', 0, G_OPTION_ARG_NONE, &dump_cache, - "Dump symbols cache stats and exit", NULL }, - { "debug", 'd', 0, G_OPTION_ARG_NONE, &is_debug, "Force debug output", - NULL }, - { "insecure", 'i', 0, G_OPTION_ARG_NONE, &is_insecure, - "Ignore running workers as privileged users (insecure)", NULL }, - { "test-lua", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &lua_tests, - "Specify lua file(s) to test", NULL }, - { "sign-config", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &sign_configs, - "Specify config file(s) to sign", NULL }, - { "private-key", 0, 0, G_OPTION_ARG_FILENAME, &privkey, - "Specify private key to sign", NULL }, - { "gen-keypair", 0, 0, G_OPTION_ARG_NONE, &gen_keypair, "Generate new encryption " - "keypair", NULL}, - { "encrypt-password", 0, 0, G_OPTION_ARG_NONE, &encrypt_password, "Encrypt " - "controller password to store in the configuration file", NULL }, - { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } -}; - -extern const struct rspamd_controller_pbkdf pbkdf_list[]; - -#ifndef HAVE_SA_SIGINFO -static void -sig_handler (gint signo) -#else -static void -sig_handler (gint signo, siginfo_t *info, void *unused) -#endif -{ -#ifdef HAVE_SA_SIGINFO - if (cur_sg < (sig_atomic_t)G_N_ELEMENTS (static_sg)) { - memcpy (&static_sg[cur_sg++], info, sizeof (siginfo_t)); - } - /* XXX: discard more than 64 simultaneous signals */ -#endif - - switch (signo) { - case SIGHUP: - do_restart = 1; - break; - case SIGINT: - case SIGTERM: - do_terminate = 1; - break; - case SIGCHLD: - child_dead = 1; - break; - case SIGUSR1: - do_reopen_log = 1; - break; - case SIGUSR2: - /* Do nothing */ - break; - case SIGALRM: - got_alarm = 1; - break; - } -} - -#ifdef HAVE_SA_SIGINFO - -static const gchar * -chldsigcode (gint code) { - switch (code) { -#ifdef CLD_EXITED - case CLD_EXITED: - return "Child exited normally"; - case CLD_KILLED: - return "Child has terminated abnormally but did not create a core file"; - case CLD_DUMPED: - return "Child has terminated abnormally and created a core file"; - case CLD_TRAPPED: - return "Traced child has trapped"; -#endif - default: - return "Unknown reason"; - } -} - -/* Prints info about incoming signals by parsing siginfo structures */ -static void -print_signals_info (void) -{ - siginfo_t *inf; - - while ((inf = g_queue_pop_head (signals_info))) { - if (inf->si_signo == SIGCHLD) { - msg_info_main ("got SIGCHLD from child: %P; reason: '%s'", - inf->si_pid, chldsigcode (inf->si_code)); - } - else { - msg_info_main ("got signal: '%s'; received from pid: %P; uid: %ul", - g_strsignal (inf->si_signo), inf->si_pid, (gulong)inf->si_uid); - } - } -} -#endif - - -static void -read_cmd_line (gint *argc, gchar ***argv, struct rspamd_config *cfg) -{ - GError *error = NULL; - GOptionContext *context; - guint i, cfg_num; - pid_t r; - - context = g_option_context_new ("- run rspamd daemon"); - g_option_context_set_summary (context, - "Summary:\n Rspamd daemon version " RVERSION "\n Release id: " RID); - g_option_context_add_main_entries (context, entries, NULL); - - if (!g_option_context_parse (context, argc, argv, &error)) { - fprintf (stderr, "option parsing failed: %s\n", error->message); - exit (1); - } - - cfg->no_fork = no_fork; - cfg->config_test = config_test; - cfg->rspamd_user = rspamd_user; - cfg->rspamd_group = rspamd_group; - cfg_num = cfg_names != NULL ? g_strv_length (cfg_names) : 0; - if (cfg_num == 0) { - cfg->cfg_name = FIXED_CONFIG_FILE; - } - else { - cfg->cfg_name = cfg_names[0]; - } - for (i = 1; i < cfg_num; i++) { - r = fork (); - if (r == 0) { - /* Spawning new main process */ - ottery_init (NULL); - cfg->cfg_name = cfg_names[i]; - (void)setsid (); - } - else if (r == -1) { - fprintf (stderr, - "fork failed while spawning process for %s configuration file: %s\n", - cfg_names[i], - strerror (errno)); - } - else { - /* Save pid to the list of other main processes, we need it to ignore SIGCHLD from them */ - g_array_append_val (other_workers, r); - } - } - cfg->pid_file = rspamd_pidfile; -} - -/* Detect privilleged mode */ -static void -detect_priv (struct rspamd_main *rspamd) -{ - struct passwd *pwd; - struct group *grp; - uid_t euid; - - euid = geteuid (); - - if (euid == 0) { - if (!rspamd->cfg->rspamd_user && !is_insecure) { - msg_err_main ( - "cannot run rspamd workers as root user, please add -u and -g options to select a proper unprivilleged user or specify --insecure flag"); - exit (EXIT_FAILURE); - } - else if (is_insecure) { - rspamd->is_privilleged = TRUE; - rspamd->workers_uid = 0; - rspamd->workers_gid = 0; - } - else { - rspamd->is_privilleged = TRUE; - pwd = getpwnam (rspamd->cfg->rspamd_user); - if (pwd == NULL) { - msg_err_main ("user specified does not exists (%s), aborting", - strerror (errno)); - exit (-errno); - } - if (rspamd->cfg->rspamd_group) { - grp = getgrnam (rspamd->cfg->rspamd_group); - if (grp == NULL) { - msg_err_main ("group specified does not exists (%s), aborting", - strerror (errno)); - exit (-errno); - } - rspamd->workers_gid = grp->gr_gid; - } - else { - rspamd->workers_gid = -1; - } - rspamd->workers_uid = pwd->pw_uid; - } - } - else { - rspamd->is_privilleged = FALSE; - rspamd->workers_uid = -1; - rspamd->workers_gid = -1; - } -} - -static void -drop_priv (struct rspamd_main *rspamd) -{ - if (rspamd->is_privilleged) { - if (setgid (rspamd->workers_gid) == -1) { - msg_err_main ("cannot setgid to %d (%s), aborting", - (gint)rspamd->workers_gid, - strerror (errno)); - exit (-errno); - } - if (rspamd->cfg->rspamd_user && - initgroups (rspamd->cfg->rspamd_user, rspamd->workers_gid) == -1) { - msg_err_main ("initgroups failed (%s), aborting", strerror (errno)); - exit (-errno); - } - if (setuid (rspamd->workers_uid) == -1) { - msg_err_main ("cannot setuid to %d (%s), aborting", - (gint)rspamd->workers_uid, - strerror (errno)); - exit (-errno); - } - } -} - -static void -config_logger (rspamd_mempool_t *pool, gpointer ud) -{ - struct rspamd_main *rm = ud; - - if (config_test) { - /* Explicitly set logger type to console in case of config testing */ - rm->cfg->log_type = RSPAMD_LOG_CONSOLE; - } - - rspamd_set_logger (rm->cfg, g_quark_try_string ("main"), rm); - if (rspamd_log_open_priv (rm->logger, rm->workers_uid, rm->workers_gid) == -1) { - fprintf (stderr, "Fatal error, cannot open logfile, exiting\n"); - exit (EXIT_FAILURE); - } -} - -static void -reread_config (struct rspamd_main *rspamd) -{ - struct rspamd_config *tmp_cfg; - gchar *cfg_file; - - tmp_cfg = (struct rspamd_config *)g_malloc0 (sizeof (struct rspamd_config)); - tmp_cfg->c_modules = g_hash_table_ref (rspamd->cfg->c_modules); - rspamd_set_logger (tmp_cfg, g_quark_try_string ("main"), rspamd); - rspamd_init_cfg (tmp_cfg, TRUE); - cfg_file = rspamd_mempool_strdup (tmp_cfg->cfg_pool, - rspamd->cfg->cfg_name); - tmp_cfg->cache = rspamd_symbols_cache_new (tmp_cfg); - /* Save some variables */ - tmp_cfg->cfg_name = cfg_file; - - if (!load_rspamd_config (tmp_cfg, FALSE)) { - rspamd_set_logger (rspamd_main->cfg, g_quark_try_string ( - "main"), rspamd_main); - msg_err_main ("cannot parse new config file, revert to old one"); - rspamd_config_free (tmp_cfg); - } - else { - msg_debug_main ("replacing config"); - rspamd_symbols_cache_destroy (rspamd_main->cfg->cache); - rspamd_config_free (rspamd->cfg); - g_free (rspamd->cfg); - - rspamd->cfg = tmp_cfg; - rspamd_set_logger (tmp_cfg, g_quark_try_string ("main"), rspamd); - /* Force debug log */ - if (is_debug) { - rspamd->cfg->log_level = G_LOG_LEVEL_DEBUG; - } - - rspamd_init_filters (rspamd->cfg, TRUE); - rspamd_symbols_cache_init (rspamd->cfg->cache); - msg_info_main ("config has been reread successfully"); - } -} - -static void -set_worker_limits (struct rspamd_worker_conf *cf) -{ - struct rlimit rlmt; - - if (cf->rlimit_nofile != 0) { - rlmt.rlim_cur = (rlim_t) cf->rlimit_nofile; - rlmt.rlim_max = (rlim_t) cf->rlimit_nofile; - - if (setrlimit (RLIMIT_NOFILE, &rlmt) == -1) { - msg_warn_main ("cannot set files rlimit: %d, %s", - cf->rlimit_nofile, - strerror (errno)); - } - } - - if (cf->rlimit_maxcore != 0) { - rlmt.rlim_cur = (rlim_t) cf->rlimit_maxcore; - rlmt.rlim_max = (rlim_t) cf->rlimit_maxcore; - - if (setrlimit (RLIMIT_CORE, &rlmt) == -1) { - msg_warn_main ("cannot set max core rlimit: %d, %s", - cf->rlimit_maxcore, - strerror (errno)); - } - } -} - -static struct rspamd_worker * -fork_worker (struct rspamd_main *rspamd, struct rspamd_worker_conf *cf, - guint index) -{ - struct rspamd_worker *cur; - /* Starting worker process */ - cur = (struct rspamd_worker *)g_malloc (sizeof (struct rspamd_worker)); - if (cur) { - bzero (cur, sizeof (struct rspamd_worker)); - cur->srv = rspamd; - cur->type = cf->type; - cur->pid = fork (); - cur->cf = g_malloc (sizeof (struct rspamd_worker_conf)); - memcpy (cur->cf, cf, sizeof (struct rspamd_worker_conf)); - cur->index = index; - cur->ctx = cf->ctx; - switch (cur->pid) { - case 0: - /* Update pid for logging */ - rspamd_log_update_pid (cf->type, rspamd->logger); - /* Lock statfile pool if possible XXX */ - /* Init PRNG after fork */ - ottery_init (NULL); - g_random_set_seed (ottery_rand_uint32 ()); - /* Drop privilleges */ - drop_priv (rspamd); - /* Set limits */ - set_worker_limits (cf); - setproctitle ("%s process", cf->worker->name); - rspamd_pidfile_close (rspamd->pfh); - /* Do silent log reopen to avoid collisions */ - rspamd_log_close (rspamd->logger); - rspamd_log_open (rspamd->logger); -#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30)) -# if (GLIB_MINOR_VERSION > 20) - /* Ugly hack for old glib */ - if (!g_thread_get_initialized ()) { - g_thread_init (NULL); - } -# else - g_thread_init (NULL); -# endif -#endif - msg_info_main ("starting %s process %P", cf->worker->name, getpid ()); - cf->worker->worker_start_func (cur); - break; - case -1: - msg_err_main ("cannot fork main process. %s", strerror (errno)); - rspamd_pidfile_remove (rspamd->pfh); - exit (-errno); - break; - default: - /* Insert worker into worker's table, pid is index */ - g_hash_table_insert (rspamd->workers, GSIZE_TO_POINTER ( - cur->pid), cur); - break; - } - } - - return cur; -} - -static void -set_alarm (guint seconds) -{ -#ifdef HAVE_SETITIMER - static struct itimerval itv; - - itv.it_interval.tv_sec = 0; - itv.it_interval.tv_usec = 0; - itv.it_value.tv_sec = seconds; - itv.it_value.tv_usec = 0; - - if (setitimer (ITIMER_REAL, &itv, NULL) == -1) { - msg_err_main ("set alarm failed: %s", strerror (errno)); - } -#else - (void)alarm (seconds); -#endif -} - -struct waiting_worker { - struct rspamd_worker_conf *cf; - guint oldindex; -}; - -static void -delay_fork (struct rspamd_worker_conf *cf, guint index) -{ - struct waiting_worker *nw; - - nw = g_slice_alloc (sizeof (*nw)); - nw->cf = cf; - nw->oldindex = index; - - workers_pending = g_list_prepend (workers_pending, nw); - set_alarm (SOFT_FORK_TIME); -} - -static GList * -create_listen_socket (GPtrArray *addrs, guint cnt, gint listen_type) -{ - GList *result = NULL; - gint fd; - guint i; - - g_ptr_array_sort (addrs, rspamd_inet_address_compare_ptr); - for (i = 0; i < cnt; i ++) { - fd = rspamd_inet_address_listen (g_ptr_array_index (addrs, i), - listen_type, TRUE); - if (fd != -1) { - result = g_list_prepend (result, GINT_TO_POINTER (fd)); - } - } - - return result; -} - -static GList * -systemd_get_socket (gint number) -{ - int sock, num_passed, flags; - GList *result = NULL; - const gchar *e; - gchar *err; - struct stat st; - /* XXX: can we trust the current choice ? */ - static const int sd_listen_fds_start = 3; - - e = getenv ("LISTEN_FDS"); - if (e != NULL) { - errno = 0; - num_passed = strtoul (e, &err, 10); - if ((err == NULL || *err == '\0') && num_passed > number) { - sock = number + sd_listen_fds_start; - if (fstat (sock, &st) == -1) { - msg_warn_main ("cannot stat systemd descriptor %d", sock); - return NULL; - } - if (!S_ISSOCK (st.st_mode)) { - msg_warn_main ("systemd descriptor %d is not a socket", sock); - errno = EINVAL; - return NULL; - } - flags = fcntl (sock, F_GETFD); - if (flags != -1) { - (void)fcntl (sock, F_SETFD, flags | FD_CLOEXEC); - } - result = g_list_prepend (result, GINT_TO_POINTER (sock)); - } - else if (num_passed <= number) { - msg_warn_main ("systemd LISTEN_FDS does not contain the expected fd: %d", - num_passed); - errno = EOVERFLOW; - } - } - else { - msg_warn_main ("cannot get systemd variable 'LISTEN_FDS'"); - errno = ENOENT; - } - - return result; -} - -static void -fork_delayed (struct rspamd_main *rspamd) -{ - GList *cur; - struct waiting_worker *w; - - while (workers_pending != NULL) { - cur = workers_pending; - w = cur->data; - - workers_pending = g_list_remove_link (workers_pending, cur); - fork_worker (rspamd, w->cf, w->oldindex); - g_list_free_1 (cur); - g_slice_free1 (sizeof (*w), w); - } -} - -static inline uintptr_t -make_listen_key (struct rspamd_worker_bind_conf *cf) -{ - XXH64_state_t st; - guint i, keylen; - guint8 *key; - rspamd_inet_addr_t *addr; - guint16 port; - - XXH64_reset (&st, rspamd_hash_seed ()); - if (cf->is_systemd) { - XXH64_update (&st, "systemd", sizeof ("systemd")); - XXH64_update (&st, &cf->cnt, sizeof (cf->cnt)); - } - else { - XXH64_update (&st, cf->name, strlen (cf->name)); - for (i = 0; i < cf->cnt; i ++) { - addr = g_ptr_array_index (cf->addrs, i); - key = rspamd_inet_address_get_radix_key ( - addr, &keylen); - XXH64_update (&st, key, keylen); - port = rspamd_inet_address_get_port (addr); - XXH64_update (&st, &port, sizeof (port)); - } - } - - return XXH64_digest (&st); -} - -static void -spawn_workers (struct rspamd_main *rspamd) -{ - GList *cur, *ls; - struct rspamd_worker_conf *cf; - gint i; - gpointer p; - guintptr key; - struct rspamd_worker_bind_conf *bcf; - gboolean listen_ok = FALSE; - - cur = rspamd->cfg->workers; - - while (cur) { - cf = cur->data; - listen_ok = FALSE; - - if (cf->worker == NULL) { - msg_err_main ("type of worker is unspecified, skip spawning"); - } - else { - if (cf->worker->has_socket) { - LL_FOREACH (cf->bind_conf, bcf) { - key = make_listen_key (bcf); - if ((p = - g_hash_table_lookup (listen_sockets, - GINT_TO_POINTER (key))) == NULL) { - if (!bcf->is_systemd) { - /* Create listen socket */ - ls = create_listen_socket (bcf->addrs, bcf->cnt, - cf->worker->listen_type); - } - else { - ls = systemd_get_socket (bcf->cnt); - } - if (ls == NULL) { - msg_err_main ("cannot listen on socket %s: %s", - bcf->name, - strerror (errno)); - } - else { - g_hash_table_insert (listen_sockets, (gpointer)key, ls); - listen_ok = TRUE; - } - } - else { - /* We had socket for this type of worker */ - ls = p; - listen_ok = TRUE; - } - /* Do not add existing lists as it causes loops */ - if (g_list_position (cf->listen_socks, ls) == -1) { - cf->listen_socks = g_list_concat (cf->listen_socks, ls); - } - } - } - - if (listen_ok) { - if (cf->worker->unique) { - if (cf->count > 1) { - msg_warn_main ("cannot spawn more than 1 %s worker, so spawn one", - cf->worker->name); - } - fork_worker (rspamd, cf, 0); - } - else if (cf->worker->threaded) { - fork_worker (rspamd, cf, 0); - } - else { - for (i = 0; i < cf->count; i++) { - fork_worker (rspamd, cf, i); - } - } - } - else { - msg_err_main ("cannot create listen socket for %s at %s", - g_quark_to_string (cf->type), cf->bind_conf->name); - - exit (EXIT_FAILURE); - } - } - - cur = g_list_next (cur); - } -} - -static void -kill_old_workers (gpointer key, gpointer value, gpointer unused) -{ - struct rspamd_worker *w = value; - - kill (w->pid, SIGUSR2); - msg_info_main ("send signal to worker %P", w->pid); -} - -static gboolean -wait_for_workers (gpointer key, gpointer value, gpointer unused) -{ - struct rspamd_worker *w = value; - gint res = 0; - - if (got_alarm) { - got_alarm = 0; - /* Set alarm for hard termination but with less time */ - set_alarm (HARD_TERMINATION_TIME / 10); - } - - if (waitpid (w->pid, &res, 0) == -1) { - if (errno == EINTR) { - got_alarm = 1; - if (w->cf->worker->killable) { - msg_info_main ("terminate worker %P with SIGKILL", w->pid); - kill (w->pid, SIGKILL); - } - else { - msg_info_main ("waiting for workers to sync"); - wait_for_workers (key, value, unused); - return TRUE; - } - } - } - - msg_info_main ("%s process %P terminated %s", g_quark_to_string ( - w->type), w->pid, - got_alarm ? "hardly" : "softly"); - g_free (w->cf); - g_free (w); - - return TRUE; -} - -static void -reopen_log_handler (gpointer key, gpointer value, gpointer unused) -{ - struct rspamd_worker *w = value; - - if (kill (w->pid, SIGUSR1) == -1) { - msg_err_main ("kill failed for pid %P: %s", w->pid, strerror (errno)); - } -} - -static gboolean -load_rspamd_config (struct rspamd_config *cfg, gboolean init_modules) -{ - cfg->cache = rspamd_symbols_cache_new (cfg); - cfg->compiled_modules = modules; - cfg->compiled_workers = workers; - - if (!rspamd_config_read (cfg, cfg->cfg_name, NULL, - config_logger, rspamd_main, vars)) { - return FALSE; - } - - /* Strictly set temp dir */ - if (!cfg->temp_dir) { - msg_warn_main ("tempdir is not set, trying to use $TMPDIR"); - cfg->temp_dir = - rspamd_mempool_strdup (cfg->cfg_pool, getenv ("TMPDIR")); - - if (!cfg->temp_dir) { - msg_warn_main ("$TMPDIR is empty too, using /tmp as default"); - cfg->temp_dir = rspamd_mempool_strdup (cfg->cfg_pool, "/tmp"); - } - } - - /* Do post-load actions */ - rspamd_config_post_load (cfg); - - if (init_modules) { - rspamd_init_filters (cfg, FALSE); - } - - return TRUE; -} - -static gint -perform_lua_tests (struct rspamd_config *cfg) -{ - gint i, tests_num, res = EXIT_SUCCESS; - gchar *cur_script; - lua_State *L = cfg->lua_state; - - tests_num = g_strv_length (lua_tests); - - for (i = 0; i < tests_num; i++) { - - if (luaL_loadfile (L, lua_tests[i]) != 0) { - msg_err_main ("load of %s failed: %s", lua_tests[i], - lua_tostring (L, -1)); - res = EXIT_FAILURE; - continue; - } - - cur_script = g_strdup (lua_tests[i]); - lua_pushstring (L, cur_script); - lua_setglobal (L, "test_script"); - lua_pushstring (L, dirname (cur_script)); - lua_setglobal (L, "test_dir"); - g_free (cur_script); - - /* do the call (0 arguments, N result) */ - if (lua_pcall (L, 0, LUA_MULTRET, 0) != 0) { - msg_info_main ("init of %s failed: %s", lua_tests[i], lua_tostring (L, - -1)); - res = EXIT_FAILURE; - continue; - } - if (lua_gettop (L) != 0) { - if (lua_tonumber (L, -1) == -1) { - msg_info_main ("%s returned -1 that indicates configuration error", - lua_tests[i]); - res = EXIT_FAILURE; - continue; - } - lua_pop (L, lua_gettop (L)); - } - } - - return res; -} - -static gint -perform_configs_sign (void) -{ -#ifndef HAVE_OPENSSL - msg_err_main ("cannot sign files without openssl support"); - return EXIT_FAILURE; -#else -# if (OPENSSL_VERSION_NUMBER < 0x10000000L) - msg_err_main ("must have openssl at least 1.0.0 to perform this action"); - return EXIT_FAILURE; -# else - gint i, tests_num, res = EXIT_SUCCESS, fd; - guint diglen; - gchar *cur_file, in_file[PATH_MAX], - out_file[PATH_MAX], dig[EVP_MAX_MD_SIZE]; - gsize siglen; - struct stat st; - gpointer map, sig; - EVP_PKEY *key = NULL; - BIO *fbio; - EVP_PKEY_CTX *key_ctx = NULL; - EVP_MD_CTX *sign_ctx = NULL; - - /* Load private key */ - fbio = BIO_new_file (privkey, "r"); - if (fbio == NULL) { - msg_err_main ("cannot open private key %s, %s", privkey, - ERR_error_string (ERR_get_error (), NULL)); - return ERR_get_error (); - } - if (!PEM_read_bio_PrivateKey (fbio, &key, rspamd_read_passphrase, NULL)) { - msg_err_main ("cannot read private key %s, %s", privkey, - ERR_error_string (ERR_get_error (), NULL)); - return ERR_get_error (); - } - - key_ctx = EVP_PKEY_CTX_new (key, NULL); - if (key_ctx == NULL) { - msg_err_main ("cannot parse private key %s, %s", privkey, - ERR_error_string (ERR_get_error (), NULL)); - return ERR_get_error (); - } - - if (EVP_PKEY_sign_init (key_ctx) <= 0) { - msg_err_main ("cannot parse private key %s, %s", privkey, - ERR_error_string (ERR_get_error (), NULL)); - return ERR_get_error (); - } - if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) { - msg_err_main ("cannot init private key %s, %s", privkey, - ERR_error_string (ERR_get_error (), NULL)); - return ERR_get_error (); - } - if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) { - msg_err_main ("cannot init signature private key %s, %s", privkey, - ERR_error_string (ERR_get_error (), NULL)); - return ERR_get_error (); - } - - sign_ctx = EVP_MD_CTX_create (); - - tests_num = g_strv_length (sign_configs); - - for (i = 0; i < tests_num; i++) { - cur_file = sign_configs[i]; - if (realpath (cur_file, in_file) == NULL) { - msg_err_main ("cannot resolve %s: %s", cur_file, strerror (errno)); - continue; - } - if (stat (in_file, &st) == -1) { - msg_err_main ("cannot stat %s: %s", in_file, strerror (errno)); - continue; - } - if ((fd = open (in_file, O_RDONLY)) == -1) { - msg_err_main ("cannot open %s: %s", in_file, strerror (errno)); - continue; - } - - if ((map = - mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, - 0)) == MAP_FAILED) { - close (fd); - msg_err_main ("cannot mmap %s: %s", in_file, strerror (errno)); - continue; - } - - close (fd); - /* Now try to sign */ - EVP_DigestInit (sign_ctx, EVP_sha256 ()); - EVP_DigestUpdate (sign_ctx, map, st.st_size); - EVP_DigestFinal (sign_ctx, dig, &diglen); - - munmap (map, st.st_size); - - if (EVP_PKEY_sign (key_ctx, NULL, &siglen, dig, diglen) <= 0) { - msg_err_main ("cannot sign %s using private key %s, %s", - in_file, - privkey, - ERR_error_string (ERR_get_error (), NULL)); - continue; - } - - sig = OPENSSL_malloc (siglen); - if (EVP_PKEY_sign (key_ctx, sig, &siglen, dig, diglen) <= 0) { - msg_err_main ("cannot sign %s using private key %s, %s", - in_file, - privkey, - ERR_error_string (ERR_get_error (), NULL)); - OPENSSL_free (sig); - continue; - } - - rspamd_snprintf (out_file, sizeof (out_file), "%s.sig", in_file); - fd = open (out_file, O_WRONLY | O_CREAT | O_TRUNC, 00644); - if (fd == -1) { - msg_err_main ("cannot open output file %s: %s", out_file, strerror ( - errno)); - OPENSSL_free (sig); - continue; - } - if (write (fd, sig, siglen) == -1) { - msg_err_main ("cannot write to output file %s: %s", out_file, - strerror (errno)); - } - OPENSSL_free (sig); - close (fd); - } - - /* Cleanup */ - EVP_MD_CTX_destroy (sign_ctx); - EVP_PKEY_CTX_free (key_ctx); - EVP_PKEY_free (key); - BIO_free (fbio); - - return res; -# endif -#endif -} - -static void -do_encrypt_password (void) -{ - const struct rspamd_controller_pbkdf *pbkdf; - guchar *salt, *key; - gchar *encoded_salt, *encoded_key; - gchar password[BUFSIZ]; - gsize plen; - - pbkdf = &pbkdf_list[0]; - g_assert (pbkdf != NULL); - - plen = rspamd_read_passphrase (password, sizeof (password), 0, NULL); - - if (plen == 0) { - fprintf (stderr, "Invalid password\n"); - exit (EXIT_FAILURE); - } - - salt = g_alloca (pbkdf->salt_len); - key = g_alloca (pbkdf->key_len); - ottery_rand_bytes (salt, pbkdf->salt_len); - /* Derive key */ - rspamd_cryptobox_pbkdf (password, strlen (password), - salt, pbkdf->salt_len, key, pbkdf->key_len, pbkdf->rounds); - - encoded_salt = rspamd_encode_base32 (salt, pbkdf->salt_len); - encoded_key = rspamd_encode_base32 (key, pbkdf->key_len); - - rspamd_printf ("$%d$%s$%s\n", pbkdf->id, encoded_salt, - encoded_key); - - g_free (encoded_salt); - g_free (encoded_key); - rspamd_explicit_memzero (password, sizeof (password)); -} - -static void -rspamd_init_main (struct rspamd_main *rspamd) -{ - rspamd->server_pool = rspamd_mempool_new (rspamd_mempool_suggest_size (), - "main"); - rspamd_main->stat = rspamd_mempool_alloc0_shared (rspamd_main->server_pool, - sizeof (struct rspamd_stat)); - /* Create rolling history */ - rspamd_main->history = rspamd_roll_history_new (rspamd_main->server_pool); -} - -gint -main (gint argc, gchar **argv, gchar **env) -{ - gint res = 0, i; - struct sigaction signals; - struct rspamd_worker *cur; - pid_t wrk; - worker_t **pworker; - GQuark type; - gpointer keypair; - GString *keypair_out; - -#ifdef HAVE_SA_SIGINFO - signals_info = g_queue_new (); -#endif -#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30)) - g_thread_init (NULL); -#endif - rspamd_main = (struct rspamd_main *)g_malloc0 (sizeof (struct rspamd_main)); - - rspamd_main->cfg = - (struct rspamd_config *)g_malloc0 (sizeof (struct rspamd_config)); - - if (!rspamd_main || !rspamd_main->cfg) { - fprintf (stderr, "Cannot allocate memory\n"); - exit (-errno); - } - -#ifndef HAVE_SETPROCTITLE - init_title (argc, argv, env); -#endif - - rspamd_init_libs (); - rspamd_init_main (rspamd_main); - rspamd_init_cfg (rspamd_main->cfg, TRUE); - - memset (&signals, 0, sizeof (struct sigaction)); - - other_workers = g_array_new (FALSE, TRUE, sizeof (pid_t)); - - read_cmd_line (&argc, &argv, rspamd_main->cfg); - - if (argc > 0) { - /* Parse variables */ - for (i = 0; i < argc; i ++) { - if (strchr (argv[i], '=') != NULL) { - gchar *k, *v, *t; - - k = g_strdup (argv[i]); - t = strchr (k, '='); - v = g_strdup (t + 1); - *t = '\0'; - - if (vars == NULL) { - vars = g_hash_table_new_full (rspamd_strcase_hash, - rspamd_strcase_equal, g_free, g_free); - } - - g_hash_table_insert (vars, k, v); - } - } - } - - if (rspamd_main->cfg->config_test || is_debug) { - rspamd_main->cfg->log_level = G_LOG_LEVEL_DEBUG; - } - else { - rspamd_main->cfg->log_level = G_LOG_LEVEL_WARNING; - } - - type = g_quark_from_static_string ("main"); - - /* First set logger to console logger */ - rspamd_main->cfg->log_type = RSPAMD_LOG_CONSOLE; - rspamd_set_logger (rspamd_main->cfg, type, rspamd_main); - (void)rspamd_log_open (rspamd_main->logger); - g_log_set_default_handler (rspamd_glib_log_function, rspamd_main->logger); - g_set_printerr_handler (rspamd_glib_printerr_function); - - detect_priv (rspamd_main); - - pworker = &workers[0]; - while (*pworker) { - /* Init string quarks */ - (void)g_quark_from_static_string ((*pworker)->name); - pworker++; - } - - /* Init listen sockets hash */ - listen_sockets = g_hash_table_new (g_direct_hash, g_direct_equal); - - /* If we want to test lua skip everything except it */ - if (lua_tests != NULL && lua_tests[0] != NULL) { - exit (perform_lua_tests (rspamd_main->cfg)); - } - - /* If we want to sign configs, just do it */ - if (sign_configs != NULL && privkey != NULL) { - exit (perform_configs_sign ()); - } - - /* Same for keypair creation */ - if (gen_keypair) { - keypair = rspamd_http_connection_gen_key (); - if (keypair == NULL) { - exit (EXIT_FAILURE); - } - keypair_out = rspamd_http_connection_print_key (keypair, - RSPAMD_KEYPAIR_PUBKEY|RSPAMD_KEYPAIR_PRIVKEY|RSPAMD_KEYPAIR_ID| - RSPAMD_KEYPAIR_BASE32|RSPAMD_KEYPAIR_HUMAN); - rspamd_printf ("%V", keypair_out); - exit (EXIT_SUCCESS); - } - - if (encrypt_password) { - do_encrypt_password (); - exit (EXIT_SUCCESS); - } - - if (rspamd_main->cfg->config_test || dump_cache) { - if (!load_rspamd_config (rspamd_main->cfg, FALSE)) { - exit (EXIT_FAILURE); - } - - res = TRUE; - - rspamd_symbols_cache_init (rspamd_main->cfg->cache); - - if (!rspamd_init_filters (rspamd_main->cfg, FALSE)) { - res = FALSE; - } - - /* Insert classifiers symbols */ - (void)rspamd_config_insert_classify_symbols (rspamd_main->cfg); - - if (!rspamd_symbols_cache_validate (rspamd_main->cfg->cache, rspamd_main->cfg, - FALSE)) { - res = FALSE; - } - if (dump_cache) { - msg_err_main ("Use rspamc counters for dumping cache"); - exit (EXIT_FAILURE); - } - fprintf (stderr, "syntax %s\n", res ? "OK" : "BAD"); - return res ? EXIT_SUCCESS : EXIT_FAILURE; - } - - /* Load config */ - if (!load_rspamd_config (rspamd_main->cfg, TRUE)) { - exit (EXIT_FAILURE); - } - - /* Override pidfile from configuration by command line argument */ - if (rspamd_pidfile != NULL) { - rspamd_main->cfg->pid_file = rspamd_pidfile; - } - - /* Force debug log */ - if (is_debug) { - rspamd_main->cfg->log_level = G_LOG_LEVEL_DEBUG; - } - - gperf_profiler_init (rspamd_main->cfg, "main"); - - msg_info_main ("rspamd " RVERSION " is starting, build id: " RID); - rspamd_main->cfg->cfg_name = rspamd_mempool_strdup ( - rspamd_main->cfg->cfg_pool, - rspamd_main->cfg->cfg_name); - - /* Daemonize */ - if (!rspamd_main->cfg->no_fork && daemon (0, 0) == -1) { - fprintf (stderr, "Cannot daemonize\n"); - exit (-errno); - } - - /* Write info */ - rspamd_main->pid = getpid (); - rspamd_main->type = type; - - rspamd_signals_init (&signals, sig_handler); - - if (rspamd_main->cfg->pid_file == NULL) { - msg_info("pid file is not specified, skipping writing it"); - } else if (rspamd_write_pid (rspamd_main) == -1) { - msg_err_main ("cannot write pid file %s", rspamd_main->cfg->pid_file); - exit (-errno); - } - - /* Block signals to use sigsuspend in future */ - sigprocmask (SIG_BLOCK, &signals.sa_mask, NULL); - - setproctitle ("main process"); - - /* Init config cache */ - rspamd_symbols_cache_init (rspamd_main->cfg->cache); - - /* Validate cache */ - (void)rspamd_symbols_cache_validate (rspamd_main->cfg->cache, rspamd_main->cfg, FALSE); - - /* Flush log */ - rspamd_log_flush (rspamd_main->logger); - - /* Maybe read roll history */ - if (rspamd_main->cfg->history_file) { - rspamd_roll_history_load (rspamd_main->history, - rspamd_main->cfg->history_file); - } - -#if defined(WITH_GPERF_TOOLS) - ProfilerStop (); -#endif - /* Spawn workers */ - rspamd_main->workers = g_hash_table_new (g_direct_hash, g_direct_equal); - spawn_workers (rspamd_main); - - /* Signal processing cycle */ - for (;; ) { - msg_debug_main ("calling sigsuspend"); - sigemptyset (&signals.sa_mask); - sigsuspend (&signals.sa_mask); -#ifdef HAVE_SA_SIGINFO - for (i = 0; i < cur_sg; i ++) { - g_queue_push_head (signals_info, &static_sg[i]); - } - cur_sg = 0; - print_signals_info (); -#endif - if (do_terminate) { - do_terminate = 0; - msg_info_main ("catch termination signal, waiting for children"); - rspamd_pass_signal (rspamd_main->workers, SIGTERM); - break; - } - if (child_dead) { - child_dead = 0; - msg_debug_main ("catch SIGCHLD signal, finding terminated worker"); - /* Remove dead child form children list */ - wrk = waitpid (0, &res, 0); - if ((cur = - g_hash_table_lookup (rspamd_main->workers, - GSIZE_TO_POINTER (wrk))) != NULL) { - /* Unlink dead process from queue and hash table */ - - g_hash_table_remove (rspamd_main->workers, GSIZE_TO_POINTER ( - wrk)); - - if (WIFEXITED (res) && WEXITSTATUS (res) == 0) { - /* Normal worker termination, do not fork one more */ - msg_info_main ("%s process %P terminated normally", - g_quark_to_string (cur->type), - cur->pid); - } - else { - if (WIFSIGNALED (res)) { - msg_warn_main ( - "%s process %P terminated abnormally by signal: %d", - g_quark_to_string (cur->type), - cur->pid, - WTERMSIG (res)); - } - else { - msg_warn_main ("%s process %P terminated abnormally", - g_quark_to_string (cur->type), - cur->pid); - } - /* Fork another worker in replace of dead one */ - delay_fork (cur->cf, cur->index); - } - - g_free (cur); - } - else { - for (i = 0; i < (gint)other_workers->len; i++) { - if (g_array_index (other_workers, pid_t, i) == wrk) { - g_array_remove_index_fast (other_workers, i); - msg_info_main ("related process %P terminated", wrk); - } - } - } - } - if (do_restart) { - do_restart = 0; - rspamd_log_reopen_priv (rspamd_main->logger, - rspamd_main->workers_uid, - rspamd_main->workers_gid); - msg_info_main ("rspamd " RVERSION " is restarting"); - g_hash_table_foreach (rspamd_main->workers, kill_old_workers, NULL); - rspamd_map_remove_all (rspamd_main->cfg); - reread_config (rspamd_main); - spawn_workers (rspamd_main); - } - if (do_reopen_log) { - do_reopen_log = 0; - rspamd_log_reopen_priv (rspamd_main->logger, - rspamd_main->workers_uid, - rspamd_main->workers_gid); - g_hash_table_foreach (rspamd_main->workers, reopen_log_handler, - NULL); - } - if (got_alarm) { - got_alarm = 0; - fork_delayed (rspamd_main); - } - } - - /* Restore some signals */ - sigemptyset (&signals.sa_mask); - sigaddset (&signals.sa_mask, SIGALRM); - sigaddset (&signals.sa_mask, SIGINT); - sigaddset (&signals.sa_mask, SIGTERM); - sigaction (SIGALRM, &signals, NULL); - sigaction (SIGTERM, &signals, NULL); - sigaction (SIGINT, &signals, NULL); - sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL); - /* Set alarm for hard termination */ - if (getenv ("G_SLICE") != NULL) { - /* Special case if we are likely running with valgrind */ - set_alarm (HARD_TERMINATION_TIME * 10); - } - else { - set_alarm (HARD_TERMINATION_TIME); - } - - /* Wait for workers termination */ - g_hash_table_foreach_remove (rspamd_main->workers, wait_for_workers, NULL); - - /* Maybe save roll history */ - if (rspamd_main->cfg->history_file) { - rspamd_roll_history_save (rspamd_main->history, - rspamd_main->cfg->history_file); - } - - msg_info_main ("terminating..."); - rspamd_symbols_cache_destroy (rspamd_main->cfg->cache); - rspamd_log_close (rspamd_main->logger); - rspamd_config_free (rspamd_main->cfg); - g_free (rspamd_main->cfg); - g_free (rspamd_main); - - g_mime_shutdown (); - -#ifdef HAVE_OPENSSL - EVP_cleanup (); - ERR_free_strings (); -#endif - - return (res); -} - -/* - * vi:ts=4 - */ diff --git a/src/main.h b/src/main.h deleted file mode 100644 index 5104040a9..000000000 --- a/src/main.h +++ /dev/null @@ -1,231 +0,0 @@ -/** - * @file main.h - * Definitions for main rspamd structures - */ - -#ifndef RSPAMD_MAIN_H -#define RSPAMD_MAIN_H - -#include "config.h" -#include "libutil/fstring.h" -#include "libutil/mem_pool.h" -#include "libutil/util.h" -#include "libutil/logger.h" -#include "libutil/http.h" -#include "libutil/upstream.h" -#include "libserver/url.h" -#include "libserver/protocol.h" -#include "libserver/buffer.h" -#include "libserver/events.h" -#include "libserver/roll_history.h" -#include "libserver/task.h" - - -/* Default values */ -#define FIXED_CONFIG_FILE RSPAMD_CONFDIR "/rspamd.conf" -/* Time in seconds to exit for old worker */ -#define SOFT_SHUTDOWN_TIME 10 - -/* Spam subject */ -#define SPAM_SUBJECT "*** SPAM *** " - -#ifdef CRLF -#undef CRLF -#undef CR -#undef LF -#endif - -#define CRLF "\r\n" -#define CR '\r' -#define LF '\n' - -/** - * Worker process structure - */ -struct rspamd_worker { - pid_t pid; /**< pid of worker */ - guint index; /**< index number */ - struct rspamd_main *srv; /**< pointer to server structure */ - GQuark type; /**< process type */ - GHashTable *signal_events; /**< signal events */ - GList *accept_events; /**< socket events */ - struct rspamd_worker_conf *cf; /**< worker config data */ - gpointer ctx; /**< worker's specific data */ -}; - -struct rspamd_worker_signal_handler { - gint signo; - gboolean enabled; - struct event ev; - struct event_base *base; - struct rspamd_worker *worker; - void (*post_handler)(void *ud); - void *handler_data; -}; - -struct rspamd_controller_pbkdf { - gint id; - guint rounds; - gsize salt_len; - gsize key_len; -}; - -/** - * Common structure representing C module context - */ -struct module_s; -struct module_ctx { - gint (*filter)(struct rspamd_task *task); /**< pointer to headers process function */ - struct module_s *mod; /**< module pointer */ - gboolean enabled; /**< true if module is enabled in configuration */ -}; - -/** - * Module - */ -typedef struct module_s { - const gchar *name; - int (*module_init_func)(struct rspamd_config *cfg, struct module_ctx **ctx); - int (*module_config_func)(struct rspamd_config *cfg); - int (*module_reconfig_func)(struct rspamd_config *cfg); - int (*module_attach_controller_func)(struct module_ctx *ctx, - GHashTable *custom_commands); -} module_t; - -typedef struct worker_s { - const gchar *name; - gpointer (*worker_init_func)(struct rspamd_config *cfg); - void (*worker_start_func)(struct rspamd_worker *worker); - gboolean has_socket; - gboolean unique; - gboolean threaded; - gboolean killable; - gint listen_type; -} worker_t; - -struct pidfh; -struct rspamd_config; -struct tokenizer; -struct rspamd_stat_classifier; -struct rspamd_classifier_config; -struct mime_part; -struct rspamd_dns_resolver; -struct rspamd_task; - -/** - * The epoch of the fuzzy client - */ -enum rspamd_fuzzy_epoch { - RSPAMD_FUZZY_EPOCH6 = 0, /**< pre 0.6.x */ - RSPAMD_FUZZY_EPOCH8, /**< 0.8 till 0.9 */ - RSPAMD_FUZZY_EPOCH9, /**< 0.9 + */ - RSPAMD_FUZZY_EPOCH_MAX -}; - -/** - * Server statistics - */ -struct rspamd_stat { - guint messages_scanned; /**< total number of messages scanned */ - guint actions_stat[METRIC_ACTION_NOACTION + 1]; /**< statistic for each action */ - guint connections_count; /**< total connections count */ - guint control_connections_count; /**< connections count to control interface */ - guint messages_learned; /**< messages learned */ - guint fuzzy_hashes; /**< number of fuzzy hashes stored */ - guint fuzzy_hashes_expired; /**< number of fuzzy hashes expired */ - guint64 fuzzy_hashes_checked[RSPAMD_FUZZY_EPOCH_MAX]; /**< ammount of check requests for each epoch */ - guint64 fuzzy_hashes_found[RSPAMD_FUZZY_EPOCH_MAX]; /**< amount of hashes found by epoch */ -}; - -/** - * Struct that determine main server object (for logging purposes) - */ -struct rspamd_main { - struct rspamd_config *cfg; /**< pointer to config structure */ - pid_t pid; /**< main pid */ - /* Pid file structure */ - rspamd_pidfh_t *pfh; /**< struct pidfh for pidfile */ - GQuark type; /**< process type */ - guint ev_initialized; /**< is event system is initialized */ - struct rspamd_stat *stat; /**< pointer to statistics */ - - rspamd_mempool_t *server_pool; /**< server's memory pool */ - GHashTable *workers; /**< workers pool indexed by pid */ - rspamd_logger_t *logger; - uid_t workers_uid; /**< worker's uid running to */ - gid_t workers_gid; /**< worker's gid running to */ - gboolean is_privilleged; /**< true if run in privilleged mode */ - struct roll_history *history; /**< rolling history */ -}; - -/** - * Structure to point exception in text from processing - */ -struct process_exception { - gsize pos; - gsize len; -}; - -/** - * Control session object - */ -struct controller_command; -struct controller_session; -typedef gboolean (*controller_func_t)(gchar **args, - struct controller_session *session); - -struct controller_session { - struct rspamd_worker *worker; /**< pointer to worker structure (controller in fact) */ - enum { - STATE_COMMAND, - STATE_HEADER, - STATE_LEARN, - STATE_LEARN_SPAM_PRE, - STATE_LEARN_SPAM, - STATE_REPLY, - STATE_QUIT, - STATE_OTHER, - STATE_WAIT, - STATE_WEIGHTS - } state; /**< current session state */ - gint sock; /**< socket descriptor */ - /* Access to authorized commands */ - gboolean authorized; /**< whether this session is authorized */ - gboolean restful; /**< whether this session is a restful session */ - GHashTable *kwargs; /**< keyword arguments for restful command */ - struct controller_command *cmd; /**< real command */ - rspamd_mempool_t *session_pool; /**< memory pool for session */ - struct rspamd_config *cfg; /**< pointer to config file */ - gchar *learn_rcpt; /**< recipient for learning */ - gchar *learn_from; /**< from address for learning */ - struct rspamd_classifier_config *learn_classifier; - gchar *learn_symbol; /**< symbol to train */ - double learn_multiplier; /**< multiplier for learning */ - rspamd_io_dispatcher_t *dispatcher; /**< IO dispatcher object */ - rspamd_fstring_t *learn_buf; /**< learn input */ - GList *parts; /**< extracted mime parts */ - gint in_class; /**< positive or negative learn */ - gboolean (*other_handler)(struct controller_session *session, - rspamd_fstring_t *in); /**< other command handler to execute at the end of processing */ - void *other_data; /**< and its data */ - controller_func_t custom_handler; /**< custom command handler */ - struct rspamd_async_session * s; /**< async session object */ - struct rspamd_task *learn_task; - struct rspamd_dns_resolver *resolver; /**< DNS resolver */ - struct event_base *ev_base; /**< Event base */ -}; - - -/** - * Register custom controller function - */ -void register_custom_controller_command (const gchar *name, - controller_func_t handler, - gboolean privilleged, - gboolean require_message); - -#endif - -/* - * vi:ts=4 - */ diff --git a/src/plugins/chartable.c b/src/plugins/chartable.c index 9c7362b35..b704e9238 100644 --- a/src/plugins/chartable.c +++ b/src/plugins/chartable.c @@ -33,7 +33,7 @@ #include "config.h" #include "libmime/message.h" -#include "main.h" +#include "rspamd.h" #define DEFAULT_SYMBOL "R_CHARSET_MIXED" #define DEFAULT_THRESHOLD 0.1 diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c index e6c88da52..7cf65f2a3 100644 --- a/src/plugins/dkim_check.c +++ b/src/plugins/dkim_check.c @@ -42,7 +42,7 @@ #include "libserver/dkim.h" #include "libutil/hash.h" #include "libutil/map.h" -#include "main.h" +#include "rspamd.h" #include "utlist.h" #define DEFAULT_SYMBOL_REJECT "R_DKIM_REJECT" diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c index 0b5cd97ed..37d5f261e 100644 --- a/src/plugins/fuzzy_check.c +++ b/src/plugins/fuzzy_check.c @@ -46,7 +46,7 @@ #include "libserver/worker_util.h" #include "fuzzy_storage.h" #include "utlist.h" -#include "main.h" +#include "rspamd.h" #include "blake2.h" #include "ottery.h" diff --git a/src/plugins/regexp.c b/src/plugins/regexp.c index 16f941364..2fd267aa0 100644 --- a/src/plugins/regexp.c +++ b/src/plugins/regexp.c @@ -33,7 +33,7 @@ #include "mime_expressions.h" #include "libutil/map.h" #include "lua/lua_common.h" -#include "main.h" +#include "rspamd.h" struct regexp_module_item { struct rspamd_expression *expr; diff --git a/src/plugins/spf.c b/src/plugins/spf.c index 70c298a7d..f19d2b50f 100644 --- a/src/plugins/spf.c +++ b/src/plugins/spf.c @@ -37,7 +37,7 @@ #include "libserver/spf.h" #include "libutil/hash.h" #include "libutil/map.h" -#include "main.h" +#include "rspamd.h" #define DEFAULT_SYMBOL_FAIL "R_SPF_FAIL" #define DEFAULT_SYMBOL_SOFTFAIL "R_SPF_SOFTFAIL" diff --git a/src/plugins/surbl.c b/src/plugins/surbl.c index 8538e57a1..882eed2d4 100644 --- a/src/plugins/surbl.c +++ b/src/plugins/surbl.c @@ -45,7 +45,7 @@ #include "libmime/message.h" #include "libutil/hash.h" #include "libutil/map.h" -#include "main.h" +#include "rspamd.h" #include "surbl.h" #include "regexp.h" #include "acism.h" diff --git a/src/plugins/surbl.h b/src/plugins/surbl.h index 76f2bafb9..9eab93000 100644 --- a/src/plugins/surbl.h +++ b/src/plugins/surbl.h @@ -3,7 +3,7 @@ #include "config.h" #include "acism.h" -#include "main.h" +#include "rspamd.h" #define DEFAULT_REDIRECTOR_PORT 8080 #define DEFAULT_SURBL_WEIGHT 10 diff --git a/src/rspamd.c b/src/rspamd.c new file mode 100644 index 000000000..48874cffe --- /dev/null +++ b/src/rspamd.c @@ -0,0 +1,1418 @@ +/* + * 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 "rspamd.h" +#include "libutil/map.h" +#include "fuzzy_storage.h" +#include "libserver/symbols_cache.h" +#include "lua/lua_common.h" +#include "libserver/worker_util.h" +#include "ottery.h" +#include "xxhash.h" +#include "utlist.h" +#include "libstat/stat_api.h" +#include "cryptobox.h" +#include "regexp.h" +#ifdef HAVE_OPENSSL +#include +#include +#include +#include +#include +#endif +#ifdef HAVE_LOCALE_H +#include +#define HAVE_SETLOCALE 1 +#endif + +#define msg_err_main(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn_main(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info_main(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_debug_main(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \ + rspamd_main->server_pool->tag.tagname, rspamd_main->server_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) + +/* 2 seconds to fork new process in place of dead one */ +#define SOFT_FORK_TIME 2 + +/* 10 seconds after getting termination signal to terminate all workers with SIGKILL */ +#define HARD_TERMINATION_TIME 10 + +static struct rspamd_worker * fork_worker (struct rspamd_main *, + struct rspamd_worker_conf *, guint); +static gboolean load_rspamd_config (struct rspamd_config *cfg, + gboolean init_modules); + +sig_atomic_t do_restart = 0; +sig_atomic_t do_reopen_log = 0; +sig_atomic_t do_terminate = 0; +sig_atomic_t child_dead = 0; +sig_atomic_t got_alarm = 0; + +#ifdef HAVE_SA_SIGINFO +GQueue *signals_info = NULL; +#endif + +static gboolean config_test = FALSE; +static gboolean no_fork = FALSE; +static gchar **cfg_names = NULL; +static gchar **lua_tests = NULL; +static gchar **sign_configs = NULL; +static gchar *privkey = NULL; +static gchar *rspamd_user = NULL; +static gchar *rspamd_group = NULL; +static gchar *rspamd_pidfile = NULL; +static gboolean dump_cache = FALSE; +static gboolean is_debug = FALSE; +static gboolean is_insecure = FALSE; +static gboolean gen_keypair = FALSE; +static gboolean encrypt_password = FALSE; +/* List of workers that are pending to start */ +static GList *workers_pending = NULL; +static GHashTable *vars = NULL; + +#ifdef HAVE_SA_SIGINFO +static siginfo_t static_sg[64]; +static sig_atomic_t cur_sg = 0; +#endif + +/* List of unrelated forked processes */ +static GArray *other_workers = NULL; + +/* List of active listen sockets indexed by worker type */ +static GHashTable *listen_sockets = NULL; + +struct rspamd_main *rspamd_main; + +/* Defined in modules.c */ +extern module_t *modules[]; +extern worker_t *workers[]; + +/* Commandline options */ +static GOptionEntry entries[] = +{ + { "config-test", 't', 0, G_OPTION_ARG_NONE, &config_test, + "Do config test and exit", NULL }, + { "no-fork", 'f', 0, G_OPTION_ARG_NONE, &no_fork, + "Do not daemonize main process", NULL }, + { "config", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &cfg_names, + "Specify config file(s)", NULL }, + { "user", 'u', 0, G_OPTION_ARG_STRING, &rspamd_user, + "User to run rspamd as", NULL }, + { "group", 'g', 0, G_OPTION_ARG_STRING, &rspamd_group, + "Group to run rspamd as", NULL }, + { "pid", 'p', 0, G_OPTION_ARG_STRING, &rspamd_pidfile, "Path to pidfile", + NULL }, + { "dump-cache", 'C', 0, G_OPTION_ARG_NONE, &dump_cache, + "Dump symbols cache stats and exit", NULL }, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &is_debug, "Force debug output", + NULL }, + { "insecure", 'i', 0, G_OPTION_ARG_NONE, &is_insecure, + "Ignore running workers as privileged users (insecure)", NULL }, + { "test-lua", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &lua_tests, + "Specify lua file(s) to test", NULL }, + { "sign-config", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &sign_configs, + "Specify config file(s) to sign", NULL }, + { "private-key", 0, 0, G_OPTION_ARG_FILENAME, &privkey, + "Specify private key to sign", NULL }, + { "gen-keypair", 0, 0, G_OPTION_ARG_NONE, &gen_keypair, "Generate new encryption " + "keypair", NULL}, + { "encrypt-password", 0, 0, G_OPTION_ARG_NONE, &encrypt_password, "Encrypt " + "controller password to store in the configuration file", NULL }, + { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } +}; + +extern const struct rspamd_controller_pbkdf pbkdf_list[]; + +#ifndef HAVE_SA_SIGINFO +static void +sig_handler (gint signo) +#else +static void +sig_handler (gint signo, siginfo_t *info, void *unused) +#endif +{ +#ifdef HAVE_SA_SIGINFO + if (cur_sg < (sig_atomic_t)G_N_ELEMENTS (static_sg)) { + memcpy (&static_sg[cur_sg++], info, sizeof (siginfo_t)); + } + /* XXX: discard more than 64 simultaneous signals */ +#endif + + switch (signo) { + case SIGHUP: + do_restart = 1; + break; + case SIGINT: + case SIGTERM: + do_terminate = 1; + break; + case SIGCHLD: + child_dead = 1; + break; + case SIGUSR1: + do_reopen_log = 1; + break; + case SIGUSR2: + /* Do nothing */ + break; + case SIGALRM: + got_alarm = 1; + break; + } +} + +#ifdef HAVE_SA_SIGINFO + +static const gchar * +chldsigcode (gint code) { + switch (code) { +#ifdef CLD_EXITED + case CLD_EXITED: + return "Child exited normally"; + case CLD_KILLED: + return "Child has terminated abnormally but did not create a core file"; + case CLD_DUMPED: + return "Child has terminated abnormally and created a core file"; + case CLD_TRAPPED: + return "Traced child has trapped"; +#endif + default: + return "Unknown reason"; + } +} + +/* Prints info about incoming signals by parsing siginfo structures */ +static void +print_signals_info (void) +{ + siginfo_t *inf; + + while ((inf = g_queue_pop_head (signals_info))) { + if (inf->si_signo == SIGCHLD) { + msg_info_main ("got SIGCHLD from child: %P; reason: '%s'", + inf->si_pid, chldsigcode (inf->si_code)); + } + else { + msg_info_main ("got signal: '%s'; received from pid: %P; uid: %ul", + g_strsignal (inf->si_signo), inf->si_pid, (gulong)inf->si_uid); + } + } +} +#endif + + +static void +read_cmd_line (gint *argc, gchar ***argv, struct rspamd_config *cfg) +{ + GError *error = NULL; + GOptionContext *context; + guint i, cfg_num; + pid_t r; + + context = g_option_context_new ("- run rspamd daemon"); + g_option_context_set_summary (context, + "Summary:\n Rspamd daemon version " RVERSION "\n Release id: " RID); + g_option_context_add_main_entries (context, entries, NULL); + + if (!g_option_context_parse (context, argc, argv, &error)) { + fprintf (stderr, "option parsing failed: %s\n", error->message); + exit (1); + } + + cfg->no_fork = no_fork; + cfg->config_test = config_test; + cfg->rspamd_user = rspamd_user; + cfg->rspamd_group = rspamd_group; + cfg_num = cfg_names != NULL ? g_strv_length (cfg_names) : 0; + if (cfg_num == 0) { + cfg->cfg_name = FIXED_CONFIG_FILE; + } + else { + cfg->cfg_name = cfg_names[0]; + } + for (i = 1; i < cfg_num; i++) { + r = fork (); + if (r == 0) { + /* Spawning new main process */ + ottery_init (NULL); + cfg->cfg_name = cfg_names[i]; + (void)setsid (); + } + else if (r == -1) { + fprintf (stderr, + "fork failed while spawning process for %s configuration file: %s\n", + cfg_names[i], + strerror (errno)); + } + else { + /* Save pid to the list of other main processes, we need it to ignore SIGCHLD from them */ + g_array_append_val (other_workers, r); + } + } + cfg->pid_file = rspamd_pidfile; +} + +/* Detect privilleged mode */ +static void +detect_priv (struct rspamd_main *rspamd) +{ + struct passwd *pwd; + struct group *grp; + uid_t euid; + + euid = geteuid (); + + if (euid == 0) { + if (!rspamd->cfg->rspamd_user && !is_insecure) { + msg_err_main ( + "cannot run rspamd workers as root user, please add -u and -g options to select a proper unprivilleged user or specify --insecure flag"); + exit (EXIT_FAILURE); + } + else if (is_insecure) { + rspamd->is_privilleged = TRUE; + rspamd->workers_uid = 0; + rspamd->workers_gid = 0; + } + else { + rspamd->is_privilleged = TRUE; + pwd = getpwnam (rspamd->cfg->rspamd_user); + if (pwd == NULL) { + msg_err_main ("user specified does not exists (%s), aborting", + strerror (errno)); + exit (-errno); + } + if (rspamd->cfg->rspamd_group) { + grp = getgrnam (rspamd->cfg->rspamd_group); + if (grp == NULL) { + msg_err_main ("group specified does not exists (%s), aborting", + strerror (errno)); + exit (-errno); + } + rspamd->workers_gid = grp->gr_gid; + } + else { + rspamd->workers_gid = -1; + } + rspamd->workers_uid = pwd->pw_uid; + } + } + else { + rspamd->is_privilleged = FALSE; + rspamd->workers_uid = -1; + rspamd->workers_gid = -1; + } +} + +static void +drop_priv (struct rspamd_main *rspamd) +{ + if (rspamd->is_privilleged) { + if (setgid (rspamd->workers_gid) == -1) { + msg_err_main ("cannot setgid to %d (%s), aborting", + (gint)rspamd->workers_gid, + strerror (errno)); + exit (-errno); + } + if (rspamd->cfg->rspamd_user && + initgroups (rspamd->cfg->rspamd_user, rspamd->workers_gid) == -1) { + msg_err_main ("initgroups failed (%s), aborting", strerror (errno)); + exit (-errno); + } + if (setuid (rspamd->workers_uid) == -1) { + msg_err_main ("cannot setuid to %d (%s), aborting", + (gint)rspamd->workers_uid, + strerror (errno)); + exit (-errno); + } + } +} + +static void +config_logger (rspamd_mempool_t *pool, gpointer ud) +{ + struct rspamd_main *rm = ud; + + if (config_test) { + /* Explicitly set logger type to console in case of config testing */ + rm->cfg->log_type = RSPAMD_LOG_CONSOLE; + } + + rspamd_set_logger (rm->cfg, g_quark_try_string ("main"), rm); + if (rspamd_log_open_priv (rm->logger, rm->workers_uid, rm->workers_gid) == -1) { + fprintf (stderr, "Fatal error, cannot open logfile, exiting\n"); + exit (EXIT_FAILURE); + } +} + +static void +reread_config (struct rspamd_main *rspamd) +{ + struct rspamd_config *tmp_cfg; + gchar *cfg_file; + + tmp_cfg = (struct rspamd_config *)g_malloc0 (sizeof (struct rspamd_config)); + tmp_cfg->c_modules = g_hash_table_ref (rspamd->cfg->c_modules); + rspamd_set_logger (tmp_cfg, g_quark_try_string ("main"), rspamd); + rspamd_init_cfg (tmp_cfg, TRUE); + cfg_file = rspamd_mempool_strdup (tmp_cfg->cfg_pool, + rspamd->cfg->cfg_name); + tmp_cfg->cache = rspamd_symbols_cache_new (tmp_cfg); + /* Save some variables */ + tmp_cfg->cfg_name = cfg_file; + + if (!load_rspamd_config (tmp_cfg, FALSE)) { + rspamd_set_logger (rspamd_main->cfg, g_quark_try_string ( + "main"), rspamd_main); + msg_err_main ("cannot parse new config file, revert to old one"); + rspamd_config_free (tmp_cfg); + } + else { + msg_debug_main ("replacing config"); + rspamd_symbols_cache_destroy (rspamd_main->cfg->cache); + rspamd_config_free (rspamd->cfg); + g_free (rspamd->cfg); + + rspamd->cfg = tmp_cfg; + rspamd_set_logger (tmp_cfg, g_quark_try_string ("main"), rspamd); + /* Force debug log */ + if (is_debug) { + rspamd->cfg->log_level = G_LOG_LEVEL_DEBUG; + } + + rspamd_init_filters (rspamd->cfg, TRUE); + rspamd_symbols_cache_init (rspamd->cfg->cache); + msg_info_main ("config has been reread successfully"); + } +} + +static void +set_worker_limits (struct rspamd_worker_conf *cf) +{ + struct rlimit rlmt; + + if (cf->rlimit_nofile != 0) { + rlmt.rlim_cur = (rlim_t) cf->rlimit_nofile; + rlmt.rlim_max = (rlim_t) cf->rlimit_nofile; + + if (setrlimit (RLIMIT_NOFILE, &rlmt) == -1) { + msg_warn_main ("cannot set files rlimit: %d, %s", + cf->rlimit_nofile, + strerror (errno)); + } + } + + if (cf->rlimit_maxcore != 0) { + rlmt.rlim_cur = (rlim_t) cf->rlimit_maxcore; + rlmt.rlim_max = (rlim_t) cf->rlimit_maxcore; + + if (setrlimit (RLIMIT_CORE, &rlmt) == -1) { + msg_warn_main ("cannot set max core rlimit: %d, %s", + cf->rlimit_maxcore, + strerror (errno)); + } + } +} + +static struct rspamd_worker * +fork_worker (struct rspamd_main *rspamd, struct rspamd_worker_conf *cf, + guint index) +{ + struct rspamd_worker *cur; + /* Starting worker process */ + cur = (struct rspamd_worker *)g_malloc (sizeof (struct rspamd_worker)); + if (cur) { + bzero (cur, sizeof (struct rspamd_worker)); + cur->srv = rspamd; + cur->type = cf->type; + cur->pid = fork (); + cur->cf = g_malloc (sizeof (struct rspamd_worker_conf)); + memcpy (cur->cf, cf, sizeof (struct rspamd_worker_conf)); + cur->index = index; + cur->ctx = cf->ctx; + switch (cur->pid) { + case 0: + /* Update pid for logging */ + rspamd_log_update_pid (cf->type, rspamd->logger); + /* Lock statfile pool if possible XXX */ + /* Init PRNG after fork */ + ottery_init (NULL); + g_random_set_seed (ottery_rand_uint32 ()); + /* Drop privilleges */ + drop_priv (rspamd); + /* Set limits */ + set_worker_limits (cf); + setproctitle ("%s process", cf->worker->name); + rspamd_pidfile_close (rspamd->pfh); + /* Do silent log reopen to avoid collisions */ + rspamd_log_close (rspamd->logger); + rspamd_log_open (rspamd->logger); +#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30)) +# if (GLIB_MINOR_VERSION > 20) + /* Ugly hack for old glib */ + if (!g_thread_get_initialized ()) { + g_thread_init (NULL); + } +# else + g_thread_init (NULL); +# endif +#endif + msg_info_main ("starting %s process %P", cf->worker->name, getpid ()); + cf->worker->worker_start_func (cur); + break; + case -1: + msg_err_main ("cannot fork main process. %s", strerror (errno)); + rspamd_pidfile_remove (rspamd->pfh); + exit (-errno); + break; + default: + /* Insert worker into worker's table, pid is index */ + g_hash_table_insert (rspamd->workers, GSIZE_TO_POINTER ( + cur->pid), cur); + break; + } + } + + return cur; +} + +static void +set_alarm (guint seconds) +{ +#ifdef HAVE_SETITIMER + static struct itimerval itv; + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = seconds; + itv.it_value.tv_usec = 0; + + if (setitimer (ITIMER_REAL, &itv, NULL) == -1) { + msg_err_main ("set alarm failed: %s", strerror (errno)); + } +#else + (void)alarm (seconds); +#endif +} + +struct waiting_worker { + struct rspamd_worker_conf *cf; + guint oldindex; +}; + +static void +delay_fork (struct rspamd_worker_conf *cf, guint index) +{ + struct waiting_worker *nw; + + nw = g_slice_alloc (sizeof (*nw)); + nw->cf = cf; + nw->oldindex = index; + + workers_pending = g_list_prepend (workers_pending, nw); + set_alarm (SOFT_FORK_TIME); +} + +static GList * +create_listen_socket (GPtrArray *addrs, guint cnt, gint listen_type) +{ + GList *result = NULL; + gint fd; + guint i; + + g_ptr_array_sort (addrs, rspamd_inet_address_compare_ptr); + for (i = 0; i < cnt; i ++) { + fd = rspamd_inet_address_listen (g_ptr_array_index (addrs, i), + listen_type, TRUE); + if (fd != -1) { + result = g_list_prepend (result, GINT_TO_POINTER (fd)); + } + } + + return result; +} + +static GList * +systemd_get_socket (gint number) +{ + int sock, num_passed, flags; + GList *result = NULL; + const gchar *e; + gchar *err; + struct stat st; + /* XXX: can we trust the current choice ? */ + static const int sd_listen_fds_start = 3; + + e = getenv ("LISTEN_FDS"); + if (e != NULL) { + errno = 0; + num_passed = strtoul (e, &err, 10); + if ((err == NULL || *err == '\0') && num_passed > number) { + sock = number + sd_listen_fds_start; + if (fstat (sock, &st) == -1) { + msg_warn_main ("cannot stat systemd descriptor %d", sock); + return NULL; + } + if (!S_ISSOCK (st.st_mode)) { + msg_warn_main ("systemd descriptor %d is not a socket", sock); + errno = EINVAL; + return NULL; + } + flags = fcntl (sock, F_GETFD); + if (flags != -1) { + (void)fcntl (sock, F_SETFD, flags | FD_CLOEXEC); + } + result = g_list_prepend (result, GINT_TO_POINTER (sock)); + } + else if (num_passed <= number) { + msg_warn_main ("systemd LISTEN_FDS does not contain the expected fd: %d", + num_passed); + errno = EOVERFLOW; + } + } + else { + msg_warn_main ("cannot get systemd variable 'LISTEN_FDS'"); + errno = ENOENT; + } + + return result; +} + +static void +fork_delayed (struct rspamd_main *rspamd) +{ + GList *cur; + struct waiting_worker *w; + + while (workers_pending != NULL) { + cur = workers_pending; + w = cur->data; + + workers_pending = g_list_remove_link (workers_pending, cur); + fork_worker (rspamd, w->cf, w->oldindex); + g_list_free_1 (cur); + g_slice_free1 (sizeof (*w), w); + } +} + +static inline uintptr_t +make_listen_key (struct rspamd_worker_bind_conf *cf) +{ + XXH64_state_t st; + guint i, keylen; + guint8 *key; + rspamd_inet_addr_t *addr; + guint16 port; + + XXH64_reset (&st, rspamd_hash_seed ()); + if (cf->is_systemd) { + XXH64_update (&st, "systemd", sizeof ("systemd")); + XXH64_update (&st, &cf->cnt, sizeof (cf->cnt)); + } + else { + XXH64_update (&st, cf->name, strlen (cf->name)); + for (i = 0; i < cf->cnt; i ++) { + addr = g_ptr_array_index (cf->addrs, i); + key = rspamd_inet_address_get_radix_key ( + addr, &keylen); + XXH64_update (&st, key, keylen); + port = rspamd_inet_address_get_port (addr); + XXH64_update (&st, &port, sizeof (port)); + } + } + + return XXH64_digest (&st); +} + +static void +spawn_workers (struct rspamd_main *rspamd) +{ + GList *cur, *ls; + struct rspamd_worker_conf *cf; + gint i; + gpointer p; + guintptr key; + struct rspamd_worker_bind_conf *bcf; + gboolean listen_ok = FALSE; + + cur = rspamd->cfg->workers; + + while (cur) { + cf = cur->data; + listen_ok = FALSE; + + if (cf->worker == NULL) { + msg_err_main ("type of worker is unspecified, skip spawning"); + } + else { + if (cf->worker->has_socket) { + LL_FOREACH (cf->bind_conf, bcf) { + key = make_listen_key (bcf); + if ((p = + g_hash_table_lookup (listen_sockets, + GINT_TO_POINTER (key))) == NULL) { + if (!bcf->is_systemd) { + /* Create listen socket */ + ls = create_listen_socket (bcf->addrs, bcf->cnt, + cf->worker->listen_type); + } + else { + ls = systemd_get_socket (bcf->cnt); + } + if (ls == NULL) { + msg_err_main ("cannot listen on socket %s: %s", + bcf->name, + strerror (errno)); + } + else { + g_hash_table_insert (listen_sockets, (gpointer)key, ls); + listen_ok = TRUE; + } + } + else { + /* We had socket for this type of worker */ + ls = p; + listen_ok = TRUE; + } + /* Do not add existing lists as it causes loops */ + if (g_list_position (cf->listen_socks, ls) == -1) { + cf->listen_socks = g_list_concat (cf->listen_socks, ls); + } + } + } + + if (listen_ok) { + if (cf->worker->unique) { + if (cf->count > 1) { + msg_warn_main ("cannot spawn more than 1 %s worker, so spawn one", + cf->worker->name); + } + fork_worker (rspamd, cf, 0); + } + else if (cf->worker->threaded) { + fork_worker (rspamd, cf, 0); + } + else { + for (i = 0; i < cf->count; i++) { + fork_worker (rspamd, cf, i); + } + } + } + else { + msg_err_main ("cannot create listen socket for %s at %s", + g_quark_to_string (cf->type), cf->bind_conf->name); + + exit (EXIT_FAILURE); + } + } + + cur = g_list_next (cur); + } +} + +static void +kill_old_workers (gpointer key, gpointer value, gpointer unused) +{ + struct rspamd_worker *w = value; + + kill (w->pid, SIGUSR2); + msg_info_main ("send signal to worker %P", w->pid); +} + +static gboolean +wait_for_workers (gpointer key, gpointer value, gpointer unused) +{ + struct rspamd_worker *w = value; + gint res = 0; + + if (got_alarm) { + got_alarm = 0; + /* Set alarm for hard termination but with less time */ + set_alarm (HARD_TERMINATION_TIME / 10); + } + + if (waitpid (w->pid, &res, 0) == -1) { + if (errno == EINTR) { + got_alarm = 1; + if (w->cf->worker->killable) { + msg_info_main ("terminate worker %P with SIGKILL", w->pid); + kill (w->pid, SIGKILL); + } + else { + msg_info_main ("waiting for workers to sync"); + wait_for_workers (key, value, unused); + return TRUE; + } + } + } + + msg_info_main ("%s process %P terminated %s", g_quark_to_string ( + w->type), w->pid, + got_alarm ? "hardly" : "softly"); + g_free (w->cf); + g_free (w); + + return TRUE; +} + +static void +reopen_log_handler (gpointer key, gpointer value, gpointer unused) +{ + struct rspamd_worker *w = value; + + if (kill (w->pid, SIGUSR1) == -1) { + msg_err_main ("kill failed for pid %P: %s", w->pid, strerror (errno)); + } +} + +static gboolean +load_rspamd_config (struct rspamd_config *cfg, gboolean init_modules) +{ + cfg->cache = rspamd_symbols_cache_new (cfg); + cfg->compiled_modules = modules; + cfg->compiled_workers = workers; + + if (!rspamd_config_read (cfg, cfg->cfg_name, NULL, + config_logger, rspamd_main, vars)) { + return FALSE; + } + + /* Strictly set temp dir */ + if (!cfg->temp_dir) { + msg_warn_main ("tempdir is not set, trying to use $TMPDIR"); + cfg->temp_dir = + rspamd_mempool_strdup (cfg->cfg_pool, getenv ("TMPDIR")); + + if (!cfg->temp_dir) { + msg_warn_main ("$TMPDIR is empty too, using /tmp as default"); + cfg->temp_dir = rspamd_mempool_strdup (cfg->cfg_pool, "/tmp"); + } + } + + /* Do post-load actions */ + rspamd_config_post_load (cfg); + + if (init_modules) { + rspamd_init_filters (cfg, FALSE); + } + + return TRUE; +} + +static gint +perform_lua_tests (struct rspamd_config *cfg) +{ + gint i, tests_num, res = EXIT_SUCCESS; + gchar *cur_script; + lua_State *L = cfg->lua_state; + + tests_num = g_strv_length (lua_tests); + + for (i = 0; i < tests_num; i++) { + + if (luaL_loadfile (L, lua_tests[i]) != 0) { + msg_err_main ("load of %s failed: %s", lua_tests[i], + lua_tostring (L, -1)); + res = EXIT_FAILURE; + continue; + } + + cur_script = g_strdup (lua_tests[i]); + lua_pushstring (L, cur_script); + lua_setglobal (L, "test_script"); + lua_pushstring (L, dirname (cur_script)); + lua_setglobal (L, "test_dir"); + g_free (cur_script); + + /* do the call (0 arguments, N result) */ + if (lua_pcall (L, 0, LUA_MULTRET, 0) != 0) { + msg_info_main ("init of %s failed: %s", lua_tests[i], lua_tostring (L, + -1)); + res = EXIT_FAILURE; + continue; + } + if (lua_gettop (L) != 0) { + if (lua_tonumber (L, -1) == -1) { + msg_info_main ("%s returned -1 that indicates configuration error", + lua_tests[i]); + res = EXIT_FAILURE; + continue; + } + lua_pop (L, lua_gettop (L)); + } + } + + return res; +} + +static gint +perform_configs_sign (void) +{ +#ifndef HAVE_OPENSSL + msg_err_main ("cannot sign files without openssl support"); + return EXIT_FAILURE; +#else +# if (OPENSSL_VERSION_NUMBER < 0x10000000L) + msg_err_main ("must have openssl at least 1.0.0 to perform this action"); + return EXIT_FAILURE; +# else + gint i, tests_num, res = EXIT_SUCCESS, fd; + guint diglen; + gchar *cur_file, in_file[PATH_MAX], + out_file[PATH_MAX], dig[EVP_MAX_MD_SIZE]; + gsize siglen; + struct stat st; + gpointer map, sig; + EVP_PKEY *key = NULL; + BIO *fbio; + EVP_PKEY_CTX *key_ctx = NULL; + EVP_MD_CTX *sign_ctx = NULL; + + /* Load private key */ + fbio = BIO_new_file (privkey, "r"); + if (fbio == NULL) { + msg_err_main ("cannot open private key %s, %s", privkey, + ERR_error_string (ERR_get_error (), NULL)); + return ERR_get_error (); + } + if (!PEM_read_bio_PrivateKey (fbio, &key, rspamd_read_passphrase, NULL)) { + msg_err_main ("cannot read private key %s, %s", privkey, + ERR_error_string (ERR_get_error (), NULL)); + return ERR_get_error (); + } + + key_ctx = EVP_PKEY_CTX_new (key, NULL); + if (key_ctx == NULL) { + msg_err_main ("cannot parse private key %s, %s", privkey, + ERR_error_string (ERR_get_error (), NULL)); + return ERR_get_error (); + } + + if (EVP_PKEY_sign_init (key_ctx) <= 0) { + msg_err_main ("cannot parse private key %s, %s", privkey, + ERR_error_string (ERR_get_error (), NULL)); + return ERR_get_error (); + } + if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) { + msg_err_main ("cannot init private key %s, %s", privkey, + ERR_error_string (ERR_get_error (), NULL)); + return ERR_get_error (); + } + if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) { + msg_err_main ("cannot init signature private key %s, %s", privkey, + ERR_error_string (ERR_get_error (), NULL)); + return ERR_get_error (); + } + + sign_ctx = EVP_MD_CTX_create (); + + tests_num = g_strv_length (sign_configs); + + for (i = 0; i < tests_num; i++) { + cur_file = sign_configs[i]; + if (realpath (cur_file, in_file) == NULL) { + msg_err_main ("cannot resolve %s: %s", cur_file, strerror (errno)); + continue; + } + if (stat (in_file, &st) == -1) { + msg_err_main ("cannot stat %s: %s", in_file, strerror (errno)); + continue; + } + if ((fd = open (in_file, O_RDONLY)) == -1) { + msg_err_main ("cannot open %s: %s", in_file, strerror (errno)); + continue; + } + + if ((map = + mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, + 0)) == MAP_FAILED) { + close (fd); + msg_err_main ("cannot mmap %s: %s", in_file, strerror (errno)); + continue; + } + + close (fd); + /* Now try to sign */ + EVP_DigestInit (sign_ctx, EVP_sha256 ()); + EVP_DigestUpdate (sign_ctx, map, st.st_size); + EVP_DigestFinal (sign_ctx, dig, &diglen); + + munmap (map, st.st_size); + + if (EVP_PKEY_sign (key_ctx, NULL, &siglen, dig, diglen) <= 0) { + msg_err_main ("cannot sign %s using private key %s, %s", + in_file, + privkey, + ERR_error_string (ERR_get_error (), NULL)); + continue; + } + + sig = OPENSSL_malloc (siglen); + if (EVP_PKEY_sign (key_ctx, sig, &siglen, dig, diglen) <= 0) { + msg_err_main ("cannot sign %s using private key %s, %s", + in_file, + privkey, + ERR_error_string (ERR_get_error (), NULL)); + OPENSSL_free (sig); + continue; + } + + rspamd_snprintf (out_file, sizeof (out_file), "%s.sig", in_file); + fd = open (out_file, O_WRONLY | O_CREAT | O_TRUNC, 00644); + if (fd == -1) { + msg_err_main ("cannot open output file %s: %s", out_file, strerror ( + errno)); + OPENSSL_free (sig); + continue; + } + if (write (fd, sig, siglen) == -1) { + msg_err_main ("cannot write to output file %s: %s", out_file, + strerror (errno)); + } + OPENSSL_free (sig); + close (fd); + } + + /* Cleanup */ + EVP_MD_CTX_destroy (sign_ctx); + EVP_PKEY_CTX_free (key_ctx); + EVP_PKEY_free (key); + BIO_free (fbio); + + return res; +# endif +#endif +} + +static void +do_encrypt_password (void) +{ + const struct rspamd_controller_pbkdf *pbkdf; + guchar *salt, *key; + gchar *encoded_salt, *encoded_key; + gchar password[BUFSIZ]; + gsize plen; + + pbkdf = &pbkdf_list[0]; + g_assert (pbkdf != NULL); + + plen = rspamd_read_passphrase (password, sizeof (password), 0, NULL); + + if (plen == 0) { + fprintf (stderr, "Invalid password\n"); + exit (EXIT_FAILURE); + } + + salt = g_alloca (pbkdf->salt_len); + key = g_alloca (pbkdf->key_len); + ottery_rand_bytes (salt, pbkdf->salt_len); + /* Derive key */ + rspamd_cryptobox_pbkdf (password, strlen (password), + salt, pbkdf->salt_len, key, pbkdf->key_len, pbkdf->rounds); + + encoded_salt = rspamd_encode_base32 (salt, pbkdf->salt_len); + encoded_key = rspamd_encode_base32 (key, pbkdf->key_len); + + rspamd_printf ("$%d$%s$%s\n", pbkdf->id, encoded_salt, + encoded_key); + + g_free (encoded_salt); + g_free (encoded_key); + rspamd_explicit_memzero (password, sizeof (password)); +} + +static void +rspamd_init_main (struct rspamd_main *rspamd) +{ + rspamd->server_pool = rspamd_mempool_new (rspamd_mempool_suggest_size (), + "main"); + rspamd_main->stat = rspamd_mempool_alloc0_shared (rspamd_main->server_pool, + sizeof (struct rspamd_stat)); + /* Create rolling history */ + rspamd_main->history = rspamd_roll_history_new (rspamd_main->server_pool); +} + +gint +main (gint argc, gchar **argv, gchar **env) +{ + gint res = 0, i; + struct sigaction signals; + struct rspamd_worker *cur; + pid_t wrk; + worker_t **pworker; + GQuark type; + gpointer keypair; + GString *keypair_out; + +#ifdef HAVE_SA_SIGINFO + signals_info = g_queue_new (); +#endif +#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION <= 30)) + g_thread_init (NULL); +#endif + rspamd_main = (struct rspamd_main *)g_malloc0 (sizeof (struct rspamd_main)); + + rspamd_main->cfg = + (struct rspamd_config *)g_malloc0 (sizeof (struct rspamd_config)); + + if (!rspamd_main || !rspamd_main->cfg) { + fprintf (stderr, "Cannot allocate memory\n"); + exit (-errno); + } + +#ifndef HAVE_SETPROCTITLE + init_title (argc, argv, env); +#endif + + rspamd_init_libs (); + rspamd_init_main (rspamd_main); + rspamd_init_cfg (rspamd_main->cfg, TRUE); + + memset (&signals, 0, sizeof (struct sigaction)); + + other_workers = g_array_new (FALSE, TRUE, sizeof (pid_t)); + + read_cmd_line (&argc, &argv, rspamd_main->cfg); + + if (argc > 0) { + /* Parse variables */ + for (i = 0; i < argc; i ++) { + if (strchr (argv[i], '=') != NULL) { + gchar *k, *v, *t; + + k = g_strdup (argv[i]); + t = strchr (k, '='); + v = g_strdup (t + 1); + *t = '\0'; + + if (vars == NULL) { + vars = g_hash_table_new_full (rspamd_strcase_hash, + rspamd_strcase_equal, g_free, g_free); + } + + g_hash_table_insert (vars, k, v); + } + } + } + + if (rspamd_main->cfg->config_test || is_debug) { + rspamd_main->cfg->log_level = G_LOG_LEVEL_DEBUG; + } + else { + rspamd_main->cfg->log_level = G_LOG_LEVEL_WARNING; + } + + type = g_quark_from_static_string ("main"); + + /* First set logger to console logger */ + rspamd_main->cfg->log_type = RSPAMD_LOG_CONSOLE; + rspamd_set_logger (rspamd_main->cfg, type, rspamd_main); + (void)rspamd_log_open (rspamd_main->logger); + g_log_set_default_handler (rspamd_glib_log_function, rspamd_main->logger); + g_set_printerr_handler (rspamd_glib_printerr_function); + + detect_priv (rspamd_main); + + pworker = &workers[0]; + while (*pworker) { + /* Init string quarks */ + (void)g_quark_from_static_string ((*pworker)->name); + pworker++; + } + + /* Init listen sockets hash */ + listen_sockets = g_hash_table_new (g_direct_hash, g_direct_equal); + + /* If we want to test lua skip everything except it */ + if (lua_tests != NULL && lua_tests[0] != NULL) { + exit (perform_lua_tests (rspamd_main->cfg)); + } + + /* If we want to sign configs, just do it */ + if (sign_configs != NULL && privkey != NULL) { + exit (perform_configs_sign ()); + } + + /* Same for keypair creation */ + if (gen_keypair) { + keypair = rspamd_http_connection_gen_key (); + if (keypair == NULL) { + exit (EXIT_FAILURE); + } + keypair_out = rspamd_http_connection_print_key (keypair, + RSPAMD_KEYPAIR_PUBKEY|RSPAMD_KEYPAIR_PRIVKEY|RSPAMD_KEYPAIR_ID| + RSPAMD_KEYPAIR_BASE32|RSPAMD_KEYPAIR_HUMAN); + rspamd_printf ("%V", keypair_out); + exit (EXIT_SUCCESS); + } + + if (encrypt_password) { + do_encrypt_password (); + exit (EXIT_SUCCESS); + } + + if (rspamd_main->cfg->config_test || dump_cache) { + if (!load_rspamd_config (rspamd_main->cfg, FALSE)) { + exit (EXIT_FAILURE); + } + + res = TRUE; + + rspamd_symbols_cache_init (rspamd_main->cfg->cache); + + if (!rspamd_init_filters (rspamd_main->cfg, FALSE)) { + res = FALSE; + } + + /* Insert classifiers symbols */ + (void)rspamd_config_insert_classify_symbols (rspamd_main->cfg); + + if (!rspamd_symbols_cache_validate (rspamd_main->cfg->cache, rspamd_main->cfg, + FALSE)) { + res = FALSE; + } + if (dump_cache) { + msg_err_main ("Use rspamc counters for dumping cache"); + exit (EXIT_FAILURE); + } + fprintf (stderr, "syntax %s\n", res ? "OK" : "BAD"); + return res ? EXIT_SUCCESS : EXIT_FAILURE; + } + + /* Load config */ + if (!load_rspamd_config (rspamd_main->cfg, TRUE)) { + exit (EXIT_FAILURE); + } + + /* Override pidfile from configuration by command line argument */ + if (rspamd_pidfile != NULL) { + rspamd_main->cfg->pid_file = rspamd_pidfile; + } + + /* Force debug log */ + if (is_debug) { + rspamd_main->cfg->log_level = G_LOG_LEVEL_DEBUG; + } + + gperf_profiler_init (rspamd_main->cfg, "main"); + + msg_info_main ("rspamd " RVERSION " is starting, build id: " RID); + rspamd_main->cfg->cfg_name = rspamd_mempool_strdup ( + rspamd_main->cfg->cfg_pool, + rspamd_main->cfg->cfg_name); + + /* Daemonize */ + if (!rspamd_main->cfg->no_fork && daemon (0, 0) == -1) { + fprintf (stderr, "Cannot daemonize\n"); + exit (-errno); + } + + /* Write info */ + rspamd_main->pid = getpid (); + rspamd_main->type = type; + + rspamd_signals_init (&signals, sig_handler); + + if (rspamd_main->cfg->pid_file == NULL) { + msg_info("pid file is not specified, skipping writing it"); + } else if (rspamd_write_pid (rspamd_main) == -1) { + msg_err_main ("cannot write pid file %s", rspamd_main->cfg->pid_file); + exit (-errno); + } + + /* Block signals to use sigsuspend in future */ + sigprocmask (SIG_BLOCK, &signals.sa_mask, NULL); + + setproctitle ("main process"); + + /* Init config cache */ + rspamd_symbols_cache_init (rspamd_main->cfg->cache); + + /* Validate cache */ + (void)rspamd_symbols_cache_validate (rspamd_main->cfg->cache, rspamd_main->cfg, FALSE); + + /* Flush log */ + rspamd_log_flush (rspamd_main->logger); + + /* Maybe read roll history */ + if (rspamd_main->cfg->history_file) { + rspamd_roll_history_load (rspamd_main->history, + rspamd_main->cfg->history_file); + } + +#if defined(WITH_GPERF_TOOLS) + ProfilerStop (); +#endif + /* Spawn workers */ + rspamd_main->workers = g_hash_table_new (g_direct_hash, g_direct_equal); + spawn_workers (rspamd_main); + + /* Signal processing cycle */ + for (;; ) { + msg_debug_main ("calling sigsuspend"); + sigemptyset (&signals.sa_mask); + sigsuspend (&signals.sa_mask); +#ifdef HAVE_SA_SIGINFO + for (i = 0; i < cur_sg; i ++) { + g_queue_push_head (signals_info, &static_sg[i]); + } + cur_sg = 0; + print_signals_info (); +#endif + if (do_terminate) { + do_terminate = 0; + msg_info_main ("catch termination signal, waiting for children"); + rspamd_pass_signal (rspamd_main->workers, SIGTERM); + break; + } + if (child_dead) { + child_dead = 0; + msg_debug_main ("catch SIGCHLD signal, finding terminated worker"); + /* Remove dead child form children list */ + wrk = waitpid (0, &res, 0); + if ((cur = + g_hash_table_lookup (rspamd_main->workers, + GSIZE_TO_POINTER (wrk))) != NULL) { + /* Unlink dead process from queue and hash table */ + + g_hash_table_remove (rspamd_main->workers, GSIZE_TO_POINTER ( + wrk)); + + if (WIFEXITED (res) && WEXITSTATUS (res) == 0) { + /* Normal worker termination, do not fork one more */ + msg_info_main ("%s process %P terminated normally", + g_quark_to_string (cur->type), + cur->pid); + } + else { + if (WIFSIGNALED (res)) { + msg_warn_main ( + "%s process %P terminated abnormally by signal: %d", + g_quark_to_string (cur->type), + cur->pid, + WTERMSIG (res)); + } + else { + msg_warn_main ("%s process %P terminated abnormally", + g_quark_to_string (cur->type), + cur->pid); + } + /* Fork another worker in replace of dead one */ + delay_fork (cur->cf, cur->index); + } + + g_free (cur); + } + else { + for (i = 0; i < (gint)other_workers->len; i++) { + if (g_array_index (other_workers, pid_t, i) == wrk) { + g_array_remove_index_fast (other_workers, i); + msg_info_main ("related process %P terminated", wrk); + } + } + } + } + if (do_restart) { + do_restart = 0; + rspamd_log_reopen_priv (rspamd_main->logger, + rspamd_main->workers_uid, + rspamd_main->workers_gid); + msg_info_main ("rspamd " RVERSION " is restarting"); + g_hash_table_foreach (rspamd_main->workers, kill_old_workers, NULL); + rspamd_map_remove_all (rspamd_main->cfg); + reread_config (rspamd_main); + spawn_workers (rspamd_main); + } + if (do_reopen_log) { + do_reopen_log = 0; + rspamd_log_reopen_priv (rspamd_main->logger, + rspamd_main->workers_uid, + rspamd_main->workers_gid); + g_hash_table_foreach (rspamd_main->workers, reopen_log_handler, + NULL); + } + if (got_alarm) { + got_alarm = 0; + fork_delayed (rspamd_main); + } + } + + /* Restore some signals */ + sigemptyset (&signals.sa_mask); + sigaddset (&signals.sa_mask, SIGALRM); + sigaddset (&signals.sa_mask, SIGINT); + sigaddset (&signals.sa_mask, SIGTERM); + sigaction (SIGALRM, &signals, NULL); + sigaction (SIGTERM, &signals, NULL); + sigaction (SIGINT, &signals, NULL); + sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL); + /* Set alarm for hard termination */ + if (getenv ("G_SLICE") != NULL) { + /* Special case if we are likely running with valgrind */ + set_alarm (HARD_TERMINATION_TIME * 10); + } + else { + set_alarm (HARD_TERMINATION_TIME); + } + + /* Wait for workers termination */ + g_hash_table_foreach_remove (rspamd_main->workers, wait_for_workers, NULL); + + /* Maybe save roll history */ + if (rspamd_main->cfg->history_file) { + rspamd_roll_history_save (rspamd_main->history, + rspamd_main->cfg->history_file); + } + + msg_info_main ("terminating..."); + rspamd_symbols_cache_destroy (rspamd_main->cfg->cache); + rspamd_log_close (rspamd_main->logger); + rspamd_config_free (rspamd_main->cfg); + g_free (rspamd_main->cfg); + g_free (rspamd_main); + + g_mime_shutdown (); + +#ifdef HAVE_OPENSSL + EVP_cleanup (); + ERR_free_strings (); +#endif + + return (res); +} + +/* + * vi:ts=4 + */ diff --git a/src/rspamd.h b/src/rspamd.h new file mode 100644 index 000000000..5104040a9 --- /dev/null +++ b/src/rspamd.h @@ -0,0 +1,231 @@ +/** + * @file main.h + * Definitions for main rspamd structures + */ + +#ifndef RSPAMD_MAIN_H +#define RSPAMD_MAIN_H + +#include "config.h" +#include "libutil/fstring.h" +#include "libutil/mem_pool.h" +#include "libutil/util.h" +#include "libutil/logger.h" +#include "libutil/http.h" +#include "libutil/upstream.h" +#include "libserver/url.h" +#include "libserver/protocol.h" +#include "libserver/buffer.h" +#include "libserver/events.h" +#include "libserver/roll_history.h" +#include "libserver/task.h" + + +/* Default values */ +#define FIXED_CONFIG_FILE RSPAMD_CONFDIR "/rspamd.conf" +/* Time in seconds to exit for old worker */ +#define SOFT_SHUTDOWN_TIME 10 + +/* Spam subject */ +#define SPAM_SUBJECT "*** SPAM *** " + +#ifdef CRLF +#undef CRLF +#undef CR +#undef LF +#endif + +#define CRLF "\r\n" +#define CR '\r' +#define LF '\n' + +/** + * Worker process structure + */ +struct rspamd_worker { + pid_t pid; /**< pid of worker */ + guint index; /**< index number */ + struct rspamd_main *srv; /**< pointer to server structure */ + GQuark type; /**< process type */ + GHashTable *signal_events; /**< signal events */ + GList *accept_events; /**< socket events */ + struct rspamd_worker_conf *cf; /**< worker config data */ + gpointer ctx; /**< worker's specific data */ +}; + +struct rspamd_worker_signal_handler { + gint signo; + gboolean enabled; + struct event ev; + struct event_base *base; + struct rspamd_worker *worker; + void (*post_handler)(void *ud); + void *handler_data; +}; + +struct rspamd_controller_pbkdf { + gint id; + guint rounds; + gsize salt_len; + gsize key_len; +}; + +/** + * Common structure representing C module context + */ +struct module_s; +struct module_ctx { + gint (*filter)(struct rspamd_task *task); /**< pointer to headers process function */ + struct module_s *mod; /**< module pointer */ + gboolean enabled; /**< true if module is enabled in configuration */ +}; + +/** + * Module + */ +typedef struct module_s { + const gchar *name; + int (*module_init_func)(struct rspamd_config *cfg, struct module_ctx **ctx); + int (*module_config_func)(struct rspamd_config *cfg); + int (*module_reconfig_func)(struct rspamd_config *cfg); + int (*module_attach_controller_func)(struct module_ctx *ctx, + GHashTable *custom_commands); +} module_t; + +typedef struct worker_s { + const gchar *name; + gpointer (*worker_init_func)(struct rspamd_config *cfg); + void (*worker_start_func)(struct rspamd_worker *worker); + gboolean has_socket; + gboolean unique; + gboolean threaded; + gboolean killable; + gint listen_type; +} worker_t; + +struct pidfh; +struct rspamd_config; +struct tokenizer; +struct rspamd_stat_classifier; +struct rspamd_classifier_config; +struct mime_part; +struct rspamd_dns_resolver; +struct rspamd_task; + +/** + * The epoch of the fuzzy client + */ +enum rspamd_fuzzy_epoch { + RSPAMD_FUZZY_EPOCH6 = 0, /**< pre 0.6.x */ + RSPAMD_FUZZY_EPOCH8, /**< 0.8 till 0.9 */ + RSPAMD_FUZZY_EPOCH9, /**< 0.9 + */ + RSPAMD_FUZZY_EPOCH_MAX +}; + +/** + * Server statistics + */ +struct rspamd_stat { + guint messages_scanned; /**< total number of messages scanned */ + guint actions_stat[METRIC_ACTION_NOACTION + 1]; /**< statistic for each action */ + guint connections_count; /**< total connections count */ + guint control_connections_count; /**< connections count to control interface */ + guint messages_learned; /**< messages learned */ + guint fuzzy_hashes; /**< number of fuzzy hashes stored */ + guint fuzzy_hashes_expired; /**< number of fuzzy hashes expired */ + guint64 fuzzy_hashes_checked[RSPAMD_FUZZY_EPOCH_MAX]; /**< ammount of check requests for each epoch */ + guint64 fuzzy_hashes_found[RSPAMD_FUZZY_EPOCH_MAX]; /**< amount of hashes found by epoch */ +}; + +/** + * Struct that determine main server object (for logging purposes) + */ +struct rspamd_main { + struct rspamd_config *cfg; /**< pointer to config structure */ + pid_t pid; /**< main pid */ + /* Pid file structure */ + rspamd_pidfh_t *pfh; /**< struct pidfh for pidfile */ + GQuark type; /**< process type */ + guint ev_initialized; /**< is event system is initialized */ + struct rspamd_stat *stat; /**< pointer to statistics */ + + rspamd_mempool_t *server_pool; /**< server's memory pool */ + GHashTable *workers; /**< workers pool indexed by pid */ + rspamd_logger_t *logger; + uid_t workers_uid; /**< worker's uid running to */ + gid_t workers_gid; /**< worker's gid running to */ + gboolean is_privilleged; /**< true if run in privilleged mode */ + struct roll_history *history; /**< rolling history */ +}; + +/** + * Structure to point exception in text from processing + */ +struct process_exception { + gsize pos; + gsize len; +}; + +/** + * Control session object + */ +struct controller_command; +struct controller_session; +typedef gboolean (*controller_func_t)(gchar **args, + struct controller_session *session); + +struct controller_session { + struct rspamd_worker *worker; /**< pointer to worker structure (controller in fact) */ + enum { + STATE_COMMAND, + STATE_HEADER, + STATE_LEARN, + STATE_LEARN_SPAM_PRE, + STATE_LEARN_SPAM, + STATE_REPLY, + STATE_QUIT, + STATE_OTHER, + STATE_WAIT, + STATE_WEIGHTS + } state; /**< current session state */ + gint sock; /**< socket descriptor */ + /* Access to authorized commands */ + gboolean authorized; /**< whether this session is authorized */ + gboolean restful; /**< whether this session is a restful session */ + GHashTable *kwargs; /**< keyword arguments for restful command */ + struct controller_command *cmd; /**< real command */ + rspamd_mempool_t *session_pool; /**< memory pool for session */ + struct rspamd_config *cfg; /**< pointer to config file */ + gchar *learn_rcpt; /**< recipient for learning */ + gchar *learn_from; /**< from address for learning */ + struct rspamd_classifier_config *learn_classifier; + gchar *learn_symbol; /**< symbol to train */ + double learn_multiplier; /**< multiplier for learning */ + rspamd_io_dispatcher_t *dispatcher; /**< IO dispatcher object */ + rspamd_fstring_t *learn_buf; /**< learn input */ + GList *parts; /**< extracted mime parts */ + gint in_class; /**< positive or negative learn */ + gboolean (*other_handler)(struct controller_session *session, + rspamd_fstring_t *in); /**< other command handler to execute at the end of processing */ + void *other_data; /**< and its data */ + controller_func_t custom_handler; /**< custom command handler */ + struct rspamd_async_session * s; /**< async session object */ + struct rspamd_task *learn_task; + struct rspamd_dns_resolver *resolver; /**< DNS resolver */ + struct event_base *ev_base; /**< Event base */ +}; + + +/** + * Register custom controller function + */ +void register_custom_controller_command (const gchar *name, + controller_func_t handler, + gboolean privilleged, + gboolean require_message); + +#endif + +/* + * vi:ts=4 + */ diff --git a/src/smtp.h b/src/smtp.h index d6713eeb2..798e87a30 100644 --- a/src/smtp.h +++ b/src/smtp.h @@ -5,7 +5,7 @@ #include "libutil/upstream.h" #include "libmime/smtp_utils.h" #include "libmime/smtp_proto.h" -#include "main.h" +#include "rspamd.h" struct rspamd_dns_resolver; diff --git a/src/smtp_proxy.c b/src/smtp_proxy.c index de9854a34..b931a7890 100644 --- a/src/smtp_proxy.c +++ b/src/smtp_proxy.c @@ -24,7 +24,7 @@ #include "config.h" #include "utlist.h" #include "libserver/proxy.h" -#include "main.h" +#include "rspamd.h" #include "smtp.h" #include "libserver/worker_util.h" diff --git a/src/worker.c b/src/worker.c index 2d37c8b5f..a127d14d8 100644 --- a/src/worker.c +++ b/src/worker.c @@ -35,7 +35,7 @@ #include "libserver/url.h" #include "libserver/dns.h" #include "libmime/message.h" -#include "main.h" +#include "rspamd.h" #include "keypairs_cache.h" #include "libstat/stat_api.h" #include "libserver/worker_util.h" diff --git a/test/rspamd_async_test.c b/test/rspamd_async_test.c index 2308acc67..71eec712a 100644 --- a/test/rspamd_async_test.c +++ b/test/rspamd_async_test.c @@ -23,7 +23,7 @@ #include "config.h" #include "tests.h" -#include "main.h" +#include "rspamd.h" #include "aio_event.h" #include "mem_pool.h" diff --git a/test/rspamd_cryptobox_test.c b/test/rspamd_cryptobox_test.c index 47eab1838..c7ad2921c 100644 --- a/test/rspamd_cryptobox_test.c +++ b/test/rspamd_cryptobox_test.c @@ -24,7 +24,7 @@ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "shingles.h" #include "fstring.h" #include "ottery.h" diff --git a/test/rspamd_dkim_test.c b/test/rspamd_dkim_test.c index c0fca4ce5..50dfae7da 100644 --- a/test/rspamd_dkim_test.c +++ b/test/rspamd_dkim_test.c @@ -23,7 +23,7 @@ #include "config.h" #include "tests.h" -#include "main.h" +#include "rspamd.h" #include "dkim.h" static const gchar test_dkim_sig[] = "v=1; a=rsa-sha256; c=relaxed/relaxed; " diff --git a/test/rspamd_dns_test.c b/test/rspamd_dns_test.c index 31efdc827..c2383b61a 100644 --- a/test/rspamd_dns_test.c +++ b/test/rspamd_dns_test.c @@ -3,7 +3,7 @@ #include "tests.h" #include "dns.h" #include "logger.h" -#include "main.h" +#include "rspamd.h" #include "events.h" #include "cfg_file.h" diff --git a/test/rspamd_http_test.c b/test/rspamd_http_test.c index ee6c49b23..f4712b224 100644 --- a/test/rspamd_http_test.c +++ b/test/rspamd_http_test.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "util.h" #include "http.h" #include "tests.h" diff --git a/test/rspamd_lua_test.c b/test/rspamd_lua_test.c index b129f1b51..caeb9814e 100644 --- a/test/rspamd_lua_test.c +++ b/test/rspamd_lua_test.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "util.h" #include "lua/lua_common.h" diff --git a/test/rspamd_radix_test.c b/test/rspamd_radix_test.c index 388f95a9a..a46f4a3b1 100644 --- a/test/rspamd_radix_test.c +++ b/test/rspamd_radix_test.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "radix.h" #include "ottery.h" diff --git a/test/rspamd_rrd_test.c b/test/rspamd_rrd_test.c index 4700be8b4..7b3cd7a68 100644 --- a/test/rspamd_rrd_test.c +++ b/test/rspamd_rrd_test.c @@ -24,7 +24,7 @@ #include "config.h" #include "tests.h" #include "rrd.h" -#include "main.h" +#include "rspamd.h" #include "ottery.h" const int rows_cnt = 20; diff --git a/test/rspamd_shingles_test.c b/test/rspamd_shingles_test.c index 1a96b64bc..433fe8c67 100644 --- a/test/rspamd_shingles_test.c +++ b/test/rspamd_shingles_test.c @@ -22,7 +22,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "shingles.h" #include "fstring.h" #include "ottery.h" diff --git a/test/rspamd_statfile_test.c b/test/rspamd_statfile_test.c index af18faa5e..2ee32ea48 100644 --- a/test/rspamd_statfile_test.c +++ b/test/rspamd_statfile_test.c @@ -1,5 +1,5 @@ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "tests.h" #include "ottery.h" diff --git a/test/rspamd_test_suite.c b/test/rspamd_test_suite.c index 8e092fd84..e10ebd889 100644 --- a/test/rspamd_test_suite.c +++ b/test/rspamd_test_suite.c @@ -1,5 +1,5 @@ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "cfg_file.h" #include "regexp.h" #include "libstat/stat_api.h" diff --git a/test/rspamd_upstream_test.c b/test/rspamd_upstream_test.c index 28c4bb757..9e04c4dda 100644 --- a/test/rspamd_upstream_test.c +++ b/test/rspamd_upstream_test.c @@ -24,7 +24,7 @@ */ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "upstream.h" #include "ottery.h" diff --git a/test/rspamd_url_test.c b/test/rspamd_url_test.c index 8acc6b324..aadc18409 100644 --- a/test/rspamd_url_test.c +++ b/test/rspamd_url_test.c @@ -1,5 +1,5 @@ #include "config.h" -#include "main.h" +#include "rspamd.h" #include "cfg_file.h" #include "url.h" #include "tests.h"