diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2020-02-10 21:03:29 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2020-02-10 21:12:13 +0000 |
commit | c607c36edda8eba5e073225ae923f80d5d22f9c6 (patch) | |
tree | 388eb673086a721aede6384cb4e935b058aae4de /src/libserver | |
parent | 03a195e7b35f75b5f0ef81a5dc1580f2962cd5ab (diff) | |
download | rspamd-c607c36edda8eba5e073225ae923f80d5d22f9c6.tar.gz rspamd-c607c36edda8eba5e073225ae923f80d5d22f9c6.zip |
[Rework] Refactor libraries structure
* Move logger implementation to libserver
* Move fuzzy backend files to a separate subdir
TODO: Move HTTP code from libutil
Diffstat (limited to 'src/libserver')
-rw-r--r-- | src/libserver/CMakeLists.txt | 15 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend.c (renamed from src/libserver/fuzzy_backend.c) | 0 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend.h (renamed from src/libserver/fuzzy_backend.h) | 0 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend_redis.c (renamed from src/libserver/fuzzy_backend_redis.c) | 0 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend_redis.h (renamed from src/libserver/fuzzy_backend_redis.h) | 0 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend_sqlite.c (renamed from src/libserver/fuzzy_backend_sqlite.c) | 0 | ||||
-rw-r--r-- | src/libserver/fuzzy_backend/fuzzy_backend_sqlite.h (renamed from src/libserver/fuzzy_backend_sqlite.h) | 0 | ||||
-rw-r--r-- | src/libserver/logger.h | 369 | ||||
-rw-r--r-- | src/libserver/logger/logger.c | 885 | ||||
-rw-r--r-- | src/libserver/logger/logger_console.c | 294 | ||||
-rw-r--r-- | src/libserver/logger/logger_file.c | 597 | ||||
-rw-r--r-- | src/libserver/logger/logger_private.h | 161 | ||||
-rw-r--r-- | src/libserver/logger/logger_syslog.c | 148 |
13 files changed, 2463 insertions, 6 deletions
diff --git a/src/libserver/CMakeLists.txt b/src/libserver/CMakeLists.txt index f0dcae867..4b999c900 100644 --- a/src/libserver/CMakeLists.txt +++ b/src/libserver/CMakeLists.txt @@ -7,22 +7,25 @@ SET(LIBRSPAMDSERVERSRC ${CMAKE_CURRENT_SOURCE_DIR}/dns.c ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_cfg.c ${CMAKE_CURRENT_SOURCE_DIR}/async_session.c - ${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend.c - ${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend_sqlite.c + ${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend/fuzzy_backend.c + ${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend/fuzzy_backend_sqlite.c + ${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend/fuzzy_backend_redis.c ${CMAKE_CURRENT_SOURCE_DIR}/html.c ${CMAKE_CURRENT_SOURCE_DIR}/milter.c ${CMAKE_CURRENT_SOURCE_DIR}/monitored.c ${CMAKE_CURRENT_SOURCE_DIR}/protocol.c ${CMAKE_CURRENT_SOURCE_DIR}/re_cache.c + ${CMAKE_CURRENT_SOURCE_DIR}/redis_pool.c ${CMAKE_CURRENT_SOURCE_DIR}/roll_history.c ${CMAKE_CURRENT_SOURCE_DIR}/spf.c ${CMAKE_CURRENT_SOURCE_DIR}/rspamd_symcache.c ${CMAKE_CURRENT_SOURCE_DIR}/task.c ${CMAKE_CURRENT_SOURCE_DIR}/url.c - ${CMAKE_CURRENT_SOURCE_DIR}/worker_util.c) - -LIST(APPEND LIBRSPAMDSERVERSRC "${CMAKE_CURRENT_SOURCE_DIR}/fuzzy_backend_redis.c") -LIST(APPEND LIBRSPAMDSERVERSRC "${CMAKE_CURRENT_SOURCE_DIR}/redis_pool.c") + ${CMAKE_CURRENT_SOURCE_DIR}/worker_util.c + ${CMAKE_CURRENT_SOURCE_DIR}/logger/logger.c + ${CMAKE_CURRENT_SOURCE_DIR}/logger/logger_file.c + ${CMAKE_CURRENT_SOURCE_DIR}/logger/logger_syslog.c + ${CMAKE_CURRENT_SOURCE_DIR}/logger/logger_console.c) # Librspamd-server SET(RSPAMD_SERVER ${LIBRSPAMDSERVERSRC} PARENT_SCOPE) diff --git a/src/libserver/fuzzy_backend.c b/src/libserver/fuzzy_backend/fuzzy_backend.c index f6dec1d6e..f6dec1d6e 100644 --- a/src/libserver/fuzzy_backend.c +++ b/src/libserver/fuzzy_backend/fuzzy_backend.c diff --git a/src/libserver/fuzzy_backend.h b/src/libserver/fuzzy_backend/fuzzy_backend.h index 23b9b68ef..23b9b68ef 100644 --- a/src/libserver/fuzzy_backend.h +++ b/src/libserver/fuzzy_backend/fuzzy_backend.h diff --git a/src/libserver/fuzzy_backend_redis.c b/src/libserver/fuzzy_backend/fuzzy_backend_redis.c index 7070773b3..7070773b3 100644 --- a/src/libserver/fuzzy_backend_redis.c +++ b/src/libserver/fuzzy_backend/fuzzy_backend_redis.c diff --git a/src/libserver/fuzzy_backend_redis.h b/src/libserver/fuzzy_backend/fuzzy_backend_redis.h index 544b20f60..544b20f60 100644 --- a/src/libserver/fuzzy_backend_redis.h +++ b/src/libserver/fuzzy_backend/fuzzy_backend_redis.h diff --git a/src/libserver/fuzzy_backend_sqlite.c b/src/libserver/fuzzy_backend/fuzzy_backend_sqlite.c index 0f9b3c1ee..0f9b3c1ee 100644 --- a/src/libserver/fuzzy_backend_sqlite.c +++ b/src/libserver/fuzzy_backend/fuzzy_backend_sqlite.c diff --git a/src/libserver/fuzzy_backend_sqlite.h b/src/libserver/fuzzy_backend/fuzzy_backend_sqlite.h index 33dc94f30..33dc94f30 100644 --- a/src/libserver/fuzzy_backend_sqlite.h +++ b/src/libserver/fuzzy_backend/fuzzy_backend_sqlite.h diff --git a/src/libserver/logger.h b/src/libserver/logger.h new file mode 100644 index 000000000..f8ecbf1ee --- /dev/null +++ b/src/libserver/logger.h @@ -0,0 +1,369 @@ +#ifndef RSPAMD_LOGGER_H +#define RSPAMD_LOGGER_H + +#include "config.h" +#include "radix.h" +#include "util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef G_LOG_LEVEL_USER_SHIFT +#define G_LOG_LEVEL_USER_SHIFT 8 +#endif + +struct rspamd_config; + +enum rspamd_log_flags { + RSPAMD_LOG_FORCED = (1 << G_LOG_LEVEL_USER_SHIFT), + RSPAMD_LOG_ENCRYPTED = (1 << (G_LOG_LEVEL_USER_SHIFT + 1)), + RSPAMD_LOG_LEVEL_MASK = ~(RSPAMD_LOG_FORCED | RSPAMD_LOG_ENCRYPTED) +}; + +typedef struct rspamd_logger_s rspamd_logger_t; +typedef bool (*rspamd_log_func_t) (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *logger, + gpointer arg); +typedef void * (*rspamd_log_init_func) (rspamd_logger_t *logger, + struct rspamd_config *cfg, + uid_t uid, gid_t gid, + GError **err); +typedef bool (*rspamd_log_on_fork_func) (rspamd_logger_t *logger, + struct rspamd_config *cfg, + gpointer arg, + GError **err); +typedef void* (*rspamd_log_reload_func) (rspamd_logger_t *logger, + struct rspamd_config *cfg, + gpointer arg, + uid_t uid, gid_t gid, + GError **err); +typedef void (*rspamd_log_dtor_func) (rspamd_logger_t *logger, + gpointer arg); + +struct rspamd_logger_funcs { + rspamd_log_init_func init; + rspamd_log_reload_func reload; + rspamd_log_dtor_func dtor; + rspamd_log_func_t log; + rspamd_log_on_fork_func on_fork; + gpointer specific; +}; + +#define RSPAMD_LOGBUF_SIZE 8192 + +/** + * Opens a new (initial) logger with console type + * This logger is also used as an emergency logger + * @return new rspamd logger object + */ +rspamd_logger_t * rspamd_log_open_emergency (rspamd_mempool_t *pool); + +/** + * Open specific (configured logging) + * @param pool + * @param config + * @param uid + * @param gid + * @return + */ +rspamd_logger_t * rspamd_log_open_specific (rspamd_mempool_t *pool, + struct rspamd_config *config, + const gchar *ptype, + uid_t uid, gid_t gid); + +/** + * Set log level (from GLogLevelFlags) + * @param logger + * @param level + */ +void rspamd_log_set_log_level (rspamd_logger_t *logger, gint level); +/** + * Set log flags (from enum rspamd_log_flags) + * @param logger + * @param flags + */ +void rspamd_log_set_log_flags (rspamd_logger_t *logger, gint flags); + +/** + * Close log file or destroy other structures + */ +void rspamd_log_close (rspamd_logger_t *logger); + + + +rspamd_logger_t * rspamd_log_default_logger (void); +rspamd_logger_t * rspamd_log_emergency_logger (void); + +/** + * Close and open log again for privileged processes + */ +bool rspamd_log_reopen (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid); + +/** + * Set log pid + */ +void rspamd_log_on_fork (GQuark ptype, struct rspamd_config *cfg, + rspamd_logger_t *logger); + +/** + * Log function that is compatible for glib messages + */ +void rspamd_glib_log_function (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer arg); + +/** + * Log function for printing glib assertions + */ +void rspamd_glib_printerr_function (const gchar *message); + +/** + * Function with variable number of arguments support + */ +bool rspamd_common_log_function (rspamd_logger_t *logger, + gint level_flags, + const gchar *module, const gchar *id, + const gchar *function, const gchar *fmt, ...); + +bool rspamd_common_logv (rspamd_logger_t *logger, gint level_flags, + const gchar *module, const gchar *id, const gchar *function, + const gchar *fmt, va_list args); + +/** + * Add new logging module, returns module ID + * @param mod + * @return + */ +guint rspamd_logger_add_debug_module (const gchar *mod); + +/* + * Macro to use for faster debug modules + */ +#define INIT_LOG_MODULE(mname) \ + static guint rspamd_##mname##_log_id = (guint)-1; \ + RSPAMD_CONSTRUCTOR(rspamd_##mname##_log_init) { \ + rspamd_##mname##_log_id = rspamd_logger_add_debug_module(#mname); \ +} + +#define INIT_LOG_MODULE_PUBLIC(mname) \ + guint rspamd_##mname##_log_id = (guint)-1; \ + RSPAMD_CONSTRUCTOR(rspamd_##mname##_log_init) { \ + rspamd_##mname##_log_id = rspamd_logger_add_debug_module(#mname); \ +} + +void rspamd_logger_configure_modules (GHashTable *mods_enabled); + +/** + * Conditional debug function + */ +bool rspamd_conditional_debug (rspamd_logger_t *logger, + rspamd_inet_addr_t *addr, const gchar *module, const gchar *id, + const gchar *function, const gchar *fmt, ...); + +bool rspamd_conditional_debug_fast (rspamd_logger_t *logger, + rspamd_inet_addr_t *addr, + guint mod_id, + const gchar *module, const gchar *id, + const gchar *function, const gchar *fmt, ...); +bool rspamd_conditional_debug_fast_num_id (rspamd_logger_t *logger, + rspamd_inet_addr_t *addr, + guint mod_id, + const gchar *module, guint64 id, + const gchar *function, const gchar *fmt, ...); + +/** + * Function with variable number of arguments support that uses static default logger + */ +bool rspamd_default_log_function (gint level_flags, + const gchar *module, const gchar *id, + const gchar *function, + const gchar *fmt, + ...); + +/** + * Varargs version of default log function + * @param log_level + * @param function + * @param fmt + * @param args + */ +bool rspamd_default_logv (gint level_flags, + const gchar *module, const gchar *id, + const gchar *function, + const gchar *fmt, + va_list args); + +/** + * Temporary turn on debug + */ +void rspamd_log_debug (rspamd_logger_t *logger); + +/** + * Turn off debug + */ +void rspamd_log_nodebug (rspamd_logger_t *logger); + +/** + * Return array of counters (4 numbers): + * 0 - errors + * 1 - warnings + * 2 - info messages + * 3 - debug messages + */ +const guint64 *rspamd_log_counters (rspamd_logger_t *logger); + +/** + * Returns errors ring buffer as ucl array + * @param logger + * @return + */ +ucl_object_t *rspamd_log_errorbuf_export (const rspamd_logger_t *logger); + +/** + * Sets new logger functions and initialise logging if needed + * @param logger + * @param nfuncs + * @return static pointer to the old functions (so this function is not reentrant) + */ +struct rspamd_logger_funcs* rspamd_logger_set_log_function (rspamd_logger_t *logger, + struct rspamd_logger_funcs *nfuncs); + +/* Typical functions */ + +extern guint rspamd_task_log_id; + +/* Logging in postfix style */ +#define msg_err(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + NULL, NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + NULL, NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + NULL, NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_notice(...) rspamd_default_log_function (G_LOG_LEVEL_MESSAGE, \ + NULL, NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_debug(...) rspamd_default_log_function (G_LOG_LEVEL_DEBUG, \ + NULL, NULL, \ + G_STRFUNC, \ + __VA_ARGS__) + +#define debug_task(...) rspamd_conditional_debug_fast (NULL, \ + task->from_addr, \ + rspamd_task_log_id, "task", task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) + +/* Use the following macros if you have `task` in the function */ +#define msg_err_task(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn_task(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_notice_task(...) rspamd_default_log_function (G_LOG_LEVEL_MESSAGE, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info_task(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_debug_task(...) rspamd_conditional_debug_fast (NULL, task->from_addr, \ + rspamd_task_log_id, "task", task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_err_task_encrypted(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL|RSPAMD_LOG_ENCRYPTED, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn_task_encrypted(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING|RSPAMD_LOG_ENCRYPTED, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_notice_task_encrypted(...) rspamd_default_log_function (G_LOG_LEVEL_MESSAGE|RSPAMD_LOG_ENCRYPTED, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info_task_encrypted(...) rspamd_default_log_function (G_LOG_LEVEL_INFO|RSPAMD_LOG_ENCRYPTED, \ + task->task_pool->tag.tagname, task->task_pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +/* Check for NULL pointer first */ +#define msg_err_task_check(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn_task_check(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info_task_check(...) rspamd_default_log_function (G_LOG_LEVEL_MESSAGE, \ + task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_notice_task_check(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + task ? task->task_pool->tag.tagname : NULL, task ? task->task_pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_debug_task_check(...) rspamd_conditional_debug_fast (NULL, \ + task ? task->from_addr : NULL, \ + rspamd_task_log_id, "task", task ? task->task_pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) + +/* Use the following macros if you have `pool` in the function */ +#define msg_err_pool(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + pool->tag.tagname, pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn_pool(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + pool->tag.tagname, pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info_pool(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + pool->tag.tagname, pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_debug_pool(...) rspamd_conditional_debug (NULL, NULL, \ + pool->tag.tagname, pool->tag.uid, \ + G_STRFUNC, \ + __VA_ARGS__) +/* Check for NULL pointer first */ +#define msg_err_pool_check(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \ + pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_warn_pool_check(...) rspamd_default_log_function (G_LOG_LEVEL_WARNING, \ + pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_info_pool_check(...) rspamd_default_log_function (G_LOG_LEVEL_INFO, \ + pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) +#define msg_debug_pool_check(...) rspamd_conditional_debug (NULL, NULL, \ + pool ? pool->tag.tagname : NULL, pool ? pool->tag.uid : NULL, \ + G_STRFUNC, \ + __VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libserver/logger/logger.c b/src/libserver/logger/logger.c new file mode 100644 index 000000000..568c0b8e9 --- /dev/null +++ b/src/libserver/logger/logger.c @@ -0,0 +1,885 @@ +/*- + * Copyright 2016 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "logger.h" +#include "rspamd.h" +#include "map.h" +#include "map_helpers.h" +#include "ottery.h" +#include "unix-std.h" +#include "logger_private.h" + + +static rspamd_logger_t *default_logger = NULL; +static rspamd_logger_t *emergency_logger = NULL; +static struct rspamd_log_modules *log_modules = NULL; + +guint rspamd_task_log_id = (guint)-1; +RSPAMD_CONSTRUCTOR(rspamd_task_log_init) +{ + rspamd_task_log_id = rspamd_logger_add_debug_module("task"); +} + +rspamd_logger_t * +rspamd_log_default_logger (void) +{ + return default_logger; +} + +rspamd_logger_t * +rspamd_log_emergency_logger (void) +{ + return emergency_logger; +} + +void +rspamd_log_set_log_level (rspamd_logger_t *logger, gint level) +{ + g_assert (logger != NULL); + + logger->log_level = level; +} + +void +rspamd_log_set_log_flags (rspamd_logger_t *logger, gint flags) +{ + g_assert (logger != NULL); + + logger->flags = flags; +} + +void +rspamd_log_close (rspamd_logger_t *logger) +{ + g_assert (logger != NULL); + + if (logger->closed) { + return; + } + + logger->closed = TRUE; + + if (logger->debug_ip) { + rspamd_map_helper_destroy_radix (logger->debug_ip); + } + + if (logger->pk) { + rspamd_pubkey_unref (logger->pk); + } + + if (logger->keypair) { + rspamd_keypair_unref (logger->keypair); + } + + logger->ops.dtor (logger, logger->ops.specific); + + /* TODO: Do we really need that ? */ + if (logger == default_logger) { + default_logger = NULL; + } + + if (logger == emergency_logger) { + emergency_logger = NULL; + } + + if (!logger->pool) { + g_free (logger); + } +} + +bool +rspamd_log_reopen (rspamd_logger_t *rspamd_log, struct rspamd_config *cfg, + uid_t uid, gid_t gid) +{ + void *nspec; + GError *err = NULL; + + g_assert (rspamd_log != NULL); + + nspec = rspamd_log->ops.reload (rspamd_log, cfg, rspamd_log->ops.specific, + uid, gid, &err); + + if (nspec != NULL) { + rspamd_log->ops.specific = nspec; + } + else { + + } + + return nspec != NULL; +} + +static void +rspamd_emergency_logger_dtor (gpointer d) +{ + rspamd_logger_t *logger = (rspamd_logger_t *)d; + + rspamd_log_close (logger); +} + +rspamd_logger_t * +rspamd_log_open_emergency (rspamd_mempool_t *pool) +{ + rspamd_logger_t *logger; + GError *err = NULL; + + g_assert (default_logger == NULL); + g_assert (emergency_logger == NULL); + + if (pool) { + logger = rspamd_mempool_alloc0 (pool, sizeof (rspamd_logger_t)); + logger->mtx = rspamd_mempool_get_mutex (pool); + } + else { + logger = g_malloc0 (sizeof (rspamd_logger_t)); + } + + + logger->pool = pool; + logger->process_type = "main"; + + const struct rspamd_logger_funcs *funcs = &console_log_funcs; + memcpy (&logger->ops, funcs, sizeof (*funcs)); + + logger->ops.specific = logger->ops.init (logger, NULL, -1, -1, &err); + + if (logger->ops.specific == NULL) { + rspamd_fprintf (stderr, "fatal error: cannot init console logging: %e\n", + err); + g_error_free (err); + + exit (EXIT_FAILURE); + } + + default_logger = logger; + emergency_logger = logger; + + rspamd_mempool_add_destructor (pool, rspamd_emergency_logger_dtor, + emergency_logger); + + return logger; +} + +rspamd_logger_t * +rspamd_log_open_specific (rspamd_mempool_t *pool, + struct rspamd_config *cfg, + const gchar *ptype, + uid_t uid, gid_t gid) +{ + rspamd_logger_t *logger; + GError *err = NULL; + + g_assert (emergency_logger != NULL); + + if (pool) { + logger = rspamd_mempool_alloc0 (pool, sizeof (rspamd_logger_t)); + logger->mtx = rspamd_mempool_get_mutex (pool); + } + else { + logger = g_malloc0 (sizeof (rspamd_logger_t)); + } + + logger->pool = pool; + + if (cfg) { + if (cfg->log_error_elts > 0 && pool) { + logger->errlog = rspamd_mempool_alloc0_shared (pool, + sizeof (*logger->errlog)); + logger->errlog->pool = pool; + logger->errlog->max_elts = cfg->log_error_elts; + logger->errlog->elt_len = cfg->log_error_elt_maxlen; + logger->errlog->elts = rspamd_mempool_alloc0_shared (pool, + sizeof (struct rspamd_logger_error_elt) * cfg->log_error_elts + + cfg->log_error_elt_maxlen * cfg->log_error_elts); + } + + logger->log_level = cfg->log_level; + logger->flags = cfg->log_flags; + + if (!(logger->flags & RSPAMD_LOG_FLAG_ENFORCED)) { + logger->log_level = cfg->log_level; + } + } + + const struct rspamd_logger_funcs *funcs = NULL; + + switch (cfg->log_type) { + case RSPAMD_LOG_CONSOLE: + funcs = &console_log_funcs; + break; + case RSPAMD_LOG_SYSLOG: + funcs = &syslog_log_funcs; + break; + case RSPAMD_LOG_FILE: + funcs = &file_log_funcs; + break; + } + + g_assert (funcs != NULL); + memcpy (&logger->ops, funcs, sizeof (*funcs)); + + logger->ops.specific = logger->ops.init (logger, cfg, uid, gid, &err); + + if (logger->ops.specific == NULL) { + rspamd_common_log_function (emergency_logger, G_LOG_LEVEL_CRITICAL, + "logger", NULL, G_STRFUNC, + "cannot open specific logger: %e", err); + g_error_free (err); + + return NULL; + } + + logger->pid = getpid (); + logger->process_type = ptype; + logger->enabled = TRUE; + + /* Set up conditional logging */ + if (cfg) { + if (cfg->debug_ip_map != NULL) { + /* Try to add it as map first of all */ + if (logger->debug_ip) { + rspamd_map_helper_destroy_radix (logger->debug_ip); + } + + logger->debug_ip = NULL; + rspamd_config_radix_from_ucl (cfg, + cfg->debug_ip_map, + "IP addresses for which debug logs are enabled", + &logger->debug_ip, + NULL, + NULL); + } + + if (cfg->log_encryption_key) { + logger->pk = rspamd_pubkey_ref (cfg->log_encryption_key); + logger->keypair = rspamd_keypair_new (RSPAMD_KEYPAIR_KEX, + RSPAMD_CRYPTOBOX_MODE_25519); + rspamd_pubkey_calculate_nm (logger->pk, logger->keypair); + } + } + + default_logger = logger; + + return logger; +} + + +/** + * Used after fork() for updating structure params + */ +void +rspamd_log_on_fork (GQuark ptype, struct rspamd_config *cfg, + rspamd_logger_t *logger) +{ + logger->pid = getpid (); + logger->process_type = g_quark_to_string (ptype); + + if (logger->ops.on_fork) { + GError *err = NULL; + + bool ret = logger->ops.on_fork (logger, cfg, logger->ops.specific, &err); + + if (!ret && emergency_logger) { + rspamd_common_log_function (emergency_logger, G_LOG_LEVEL_CRITICAL, + "logger", NULL, G_STRFUNC, + "cannot update logging on fork: %e", err); + g_error_free (err); + } + } +} + +static inline gboolean +rspamd_logger_need_log (rspamd_logger_t *rspamd_log, GLogLevelFlags log_level, + guint module_id) +{ + g_assert (rspamd_log != NULL); + + if ((log_level & RSPAMD_LOG_FORCED) || + (log_level & (RSPAMD_LOG_LEVEL_MASK & G_LOG_LEVEL_MASK)) <= rspamd_log->log_level) { + return TRUE; + } + + if (module_id != (guint)-1 && isset (log_modules->bitset, module_id)) { + return TRUE; + } + + return FALSE; +} + +static gchar * +rspamd_log_encrypt_message (const gchar *begin, const gchar *end, gsize *enc_len, + rspamd_logger_t *rspamd_log) +{ + guchar *out; + gchar *b64; + guchar *p, *nonce, *mac; + const guchar *comp; + guint len, inlen; + + g_assert (end > begin); + /* base64 (pubkey | nonce | message) */ + inlen = rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519) + + rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519) + + rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519) + + (end - begin); + out = g_malloc (inlen); + + p = out; + comp = rspamd_pubkey_get_pk (rspamd_log->pk, &len); + memcpy (p, comp, len); + p += len; + ottery_rand_bytes (p, rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519)); + nonce = p; + p += rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519); + mac = p; + p += rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519); + memcpy (p, begin, end - begin); + comp = rspamd_pubkey_get_nm (rspamd_log->pk, rspamd_log->keypair); + g_assert (comp != NULL); + rspamd_cryptobox_encrypt_nm_inplace (p, end - begin, nonce, comp, mac, + RSPAMD_CRYPTOBOX_MODE_25519); + b64 = rspamd_encode_base64 (out, inlen, 0, enc_len); + g_free (out); + + return b64; +} + +static void +rspamd_log_write_ringbuffer (rspamd_logger_t *rspamd_log, + const gchar *module, const gchar *id, + const gchar *data, glong len) +{ + guint32 row_num; + struct rspamd_logger_error_log *elog; + struct rspamd_logger_error_elt *elt; + + if (!rspamd_log->errlog) { + return; + } + + elog = rspamd_log->errlog; + + g_atomic_int_compare_and_exchange (&elog->cur_row, elog->max_elts, 0); +#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION > 30)) + row_num = g_atomic_int_add (&elog->cur_row, 1); +#else + row_num = g_atomic_int_exchange_and_add (&elog->cur_row, 1); +#endif + + if (row_num < elog->max_elts) { + elt = (struct rspamd_logger_error_elt *)(((guchar *)elog->elts) + + (sizeof (*elt) + elog->elt_len) * row_num); + g_atomic_int_set (&elt->completed, 0); + } + else { + /* Race condition */ + elog->cur_row = 0; + return; + } + + elt->pid = rspamd_log->pid; + elt->ptype = g_quark_from_string (rspamd_log->process_type); + elt->ts = rspamd_get_calendar_ticks (); + + if (id) { + rspamd_strlcpy (elt->id, id, sizeof (elt->id)); + } + else { + rspamd_strlcpy (elt->id, "", sizeof (elt->id)); + } + + if (module) { + rspamd_strlcpy (elt->module, module, sizeof (elt->module)); + } + else { + rspamd_strlcpy (elt->module, "", sizeof (elt->module)); + } + + rspamd_strlcpy (elt->message, data, MIN (len + 1, elog->elt_len)); + g_atomic_int_set (&elt->completed, 1); +} + +bool +rspamd_common_logv (rspamd_logger_t *rspamd_log, gint level_flags, + const gchar *module, const gchar *id, const gchar *function, + const gchar *fmt, va_list args) +{ + gchar logbuf[RSPAMD_LOGBUF_SIZE], *end; + gint level = level_flags & (RSPAMD_LOG_LEVEL_MASK & G_LOG_LEVEL_MASK), mod_id; + bool ret = false; + + if (G_UNLIKELY (rspamd_log == NULL)) { + rspamd_log = default_logger; + } + + if (G_UNLIKELY (rspamd_log == NULL)) { + /* Just fprintf message to stderr */ + if (level >= G_LOG_LEVEL_INFO) { + end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, args); + rspamd_fprintf (stderr, "%*s\n", (gint)(end - logbuf), logbuf); + } + } + else { + if (level == G_LOG_LEVEL_DEBUG) { + mod_id = rspamd_logger_add_debug_module (module); + } + else { + mod_id = -1; + } + + if (rspamd_logger_need_log (rspamd_log, level_flags, mod_id)) { + end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, args); + + if ((level_flags & RSPAMD_LOG_ENCRYPTED) && rspamd_log->pk) { + gchar *encrypted; + gsize enc_len; + + encrypted = rspamd_log_encrypt_message (logbuf, end, &enc_len, + rspamd_log); + ret = rspamd_log->ops.log (module, id, + function, + level_flags, + encrypted, + enc_len, + rspamd_log, + rspamd_log->ops.specific); + g_free (encrypted); + } + else { + ret = rspamd_log->ops.log (module, id, + function, + level_flags, + logbuf, + end - logbuf, + rspamd_log, + rspamd_log->ops.specific); + } + + switch (level) { + case G_LOG_LEVEL_CRITICAL: + rspamd_log->log_cnt[0] ++; + rspamd_log_write_ringbuffer (rspamd_log, module, id, logbuf, + end - logbuf); + break; + case G_LOG_LEVEL_WARNING: + rspamd_log->log_cnt[1]++; + break; + case G_LOG_LEVEL_INFO: + rspamd_log->log_cnt[2]++; + break; + case G_LOG_LEVEL_DEBUG: + rspamd_log->log_cnt[3]++; + break; + default: + break; + } + } + } + + return ret; +} + +/** + * This log functions select real logger and write message if level is less or equal to configured log level + */ +bool +rspamd_common_log_function (rspamd_logger_t *rspamd_log, + gint level_flags, + const gchar *module, const gchar *id, + const gchar *function, + const gchar *fmt, + ...) +{ + va_list vp; + + va_start (vp, fmt); + bool ret = rspamd_common_logv (rspamd_log, level_flags, module, id, function, fmt, vp); + va_end (vp); + + return ret; +} + +bool +rspamd_default_logv (gint level_flags, const gchar *module, const gchar *id, + const gchar *function, + const gchar *fmt, va_list args) +{ + return rspamd_common_logv (NULL, level_flags, module, id, function, fmt, args); +} + +bool +rspamd_default_log_function (gint level_flags, + const gchar *module, const gchar *id, + const gchar *function, const gchar *fmt, ...) +{ + + va_list vp; + + va_start (vp, fmt); + bool ret = rspamd_default_logv (level_flags, module, id, function, fmt, vp); + va_end (vp); + + return ret; +} + + +/** + * Main file interface for logging + */ +/** + * Write log line depending on ip + */ +bool +rspamd_conditional_debug (rspamd_logger_t *rspamd_log, + rspamd_inet_addr_t *addr, const gchar *module, const gchar *id, + const gchar *function, const gchar *fmt, ...) +{ + static gchar logbuf[LOGBUF_LEN]; + va_list vp; + gchar *end; + guint mod_id; + + if (rspamd_log == NULL) { + rspamd_log = default_logger; + } + + mod_id = rspamd_logger_add_debug_module (module); + + if (rspamd_logger_need_log (rspamd_log, G_LOG_LEVEL_DEBUG, mod_id) || + rspamd_log->is_debug) { + if (rspamd_log->debug_ip && addr != NULL) { + if (rspamd_match_radix_map_addr (rspamd_log->debug_ip, + addr) == NULL) { + return false; + } + } + + va_start (vp, fmt); + end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, vp); + *end = '\0'; + va_end (vp); + return rspamd_log->ops.log (module, id, + function, + G_LOG_LEVEL_DEBUG | RSPAMD_LOG_FORCED, + logbuf, + end - logbuf, + rspamd_log, + rspamd_log->ops.specific); + } + + return false; +} + +bool +rspamd_conditional_debug_fast (rspamd_logger_t *rspamd_log, + rspamd_inet_addr_t *addr, + guint mod_id, const gchar *module, const gchar *id, + const gchar *function, const gchar *fmt, ...) +{ + static gchar logbuf[LOGBUF_LEN]; + va_list vp; + gchar *end; + + if (rspamd_log == NULL) { + rspamd_log = default_logger; + } + + if (rspamd_logger_need_log (rspamd_log, G_LOG_LEVEL_DEBUG, mod_id) || + rspamd_log->is_debug) { + if (rspamd_log->debug_ip && addr != NULL) { + if (rspamd_match_radix_map_addr (rspamd_log->debug_ip, addr) + == NULL) { + return false; + } + } + + va_start (vp, fmt); + end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, vp); + *end = '\0'; + va_end (vp); + return rspamd_log->ops.log (module, id, + function, + G_LOG_LEVEL_DEBUG | RSPAMD_LOG_FORCED, + logbuf, + end - logbuf, + rspamd_log, + rspamd_log->ops.specific); + } + + return false; +} + +bool +rspamd_conditional_debug_fast_num_id (rspamd_logger_t *rspamd_log, + rspamd_inet_addr_t *addr, + guint mod_id, const gchar *module, guint64 id, + const gchar *function, const gchar *fmt, ...) +{ + static gchar logbuf[LOGBUF_LEN], idbuf[64]; + va_list vp; + gchar *end; + + if (rspamd_log == NULL) { + rspamd_log = default_logger; + } + + if (rspamd_logger_need_log (rspamd_log, G_LOG_LEVEL_DEBUG, mod_id) || + rspamd_log->is_debug) { + if (rspamd_log->debug_ip && addr != NULL) { + if (rspamd_match_radix_map_addr (rspamd_log->debug_ip, addr) + == NULL) { + return false; + } + } + + rspamd_snprintf (idbuf, sizeof (idbuf), "%XuL", id); + va_start (vp, fmt); + end = rspamd_vsnprintf (logbuf, sizeof (logbuf), fmt, vp); + *end = '\0'; + va_end (vp); + return rspamd_log->ops.log (module, idbuf, + function, + G_LOG_LEVEL_DEBUG | RSPAMD_LOG_FORCED, + logbuf, + end - logbuf, + rspamd_log, + rspamd_log->ops.specific); + } + + return false; +} + +/** + * Wrapper for glib logger + */ +void +rspamd_glib_log_function (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer arg) +{ + rspamd_logger_t *rspamd_log = (rspamd_logger_t *)arg; + + if (rspamd_log->enabled && + rspamd_logger_need_log (rspamd_log, log_level, -1)) { + rspamd_log->ops.log ("glib", NULL, + NULL, + log_level, + message, + strlen (message), + rspamd_log, + rspamd_log->ops.specific); + } +} + +void +rspamd_glib_printerr_function (const gchar *message) +{ + rspamd_common_log_function (NULL, G_LOG_LEVEL_CRITICAL, "glib", + NULL, G_STRFUNC, + "%s", message); +} + +/** + * Temporary turn on debugging + */ +void +rspamd_log_debug (rspamd_logger_t *rspamd_log) +{ + rspamd_log->is_debug = TRUE; +} + +/** + * Turn off temporary debugging + */ +void +rspamd_log_nodebug (rspamd_logger_t *rspamd_log) +{ + rspamd_log->is_debug = FALSE; +} + +const guint64 * +rspamd_log_counters (rspamd_logger_t *logger) +{ + if (logger) { + return logger->log_cnt; + } + + return NULL; +} + +static gint +rspamd_log_errlog_cmp (const ucl_object_t **o1, const ucl_object_t **o2) +{ + const ucl_object_t *ts1, *ts2; + + ts1 = ucl_object_lookup (*o1, "ts"); + ts2 = ucl_object_lookup (*o2, "ts"); + + if (ts1 && ts2) { + gdouble t1 = ucl_object_todouble (ts1), t2 = ucl_object_todouble (ts2); + + if (t1 > t2) { + return -1; + } + else if (t2 > t1) { + return 1; + } + } + + return 0; +} + +ucl_object_t * +rspamd_log_errorbuf_export (const rspamd_logger_t *logger) +{ + struct rspamd_logger_error_elt *cpy, *cur; + ucl_object_t *top = ucl_object_typed_new (UCL_ARRAY); + guint i; + + if (logger->errlog == NULL) { + return top; + } + + cpy = g_malloc0_n (logger->errlog->max_elts, + sizeof (*cpy) + logger->errlog->elt_len); + memcpy (cpy, logger->errlog->elts, logger->errlog->max_elts * + (sizeof (*cpy) + logger->errlog->elt_len)); + + for (i = 0; i < logger->errlog->max_elts; i ++) { + cur = (struct rspamd_logger_error_elt *)((guchar *)cpy + + i * ((sizeof (*cpy) + logger->errlog->elt_len))); + if (cur->completed) { + ucl_object_t *obj = ucl_object_typed_new (UCL_OBJECT); + + ucl_object_insert_key (obj, ucl_object_fromdouble (cur->ts), + "ts", 0, false); + ucl_object_insert_key (obj, ucl_object_fromint (cur->pid), + "pid", 0, false); + ucl_object_insert_key (obj, + ucl_object_fromstring (g_quark_to_string (cur->ptype)), + "type", 0, false); + ucl_object_insert_key (obj, ucl_object_fromstring (cur->id), + "id", 0, false); + ucl_object_insert_key (obj, ucl_object_fromstring (cur->module), + "module", 0, false); + ucl_object_insert_key (obj, ucl_object_fromstring (cur->message), + "message", 0, false); + + ucl_array_append (top, obj); + } + } + + ucl_object_array_sort (top, rspamd_log_errlog_cmp); + g_free (cpy); + + return top; +} + +static guint +rspamd_logger_allocate_mod_bit (void) +{ + if (log_modules->bitset_allocated * NBBY > log_modules->bitset_len + 1) { + log_modules->bitset_len ++; + return log_modules->bitset_len - 1; + } + else { + /* Need to expand */ + log_modules->bitset_allocated *= 2; + log_modules->bitset = g_realloc (log_modules->bitset, + log_modules->bitset_allocated); + + return rspamd_logger_allocate_mod_bit (); + } +} + +RSPAMD_DESTRUCTOR (rspamd_debug_modules_dtor) +{ + if (log_modules) { + g_hash_table_unref (log_modules->modules); + g_free (log_modules->bitset); + g_free (log_modules); + } +} + +guint +rspamd_logger_add_debug_module (const gchar *mname) +{ + struct rspamd_log_module *m; + + if (mname == NULL) { + return (guint)-1; + } + + if (log_modules == NULL) { + /* + * This is usually called from constructors, so we call init check + * each time to avoid dependency issues between ctors calls + */ + log_modules = g_malloc0 (sizeof (*log_modules)); + log_modules->modules = g_hash_table_new_full (rspamd_strcase_hash, + rspamd_strcase_equal, g_free, g_free); + log_modules->bitset_allocated = 16; + log_modules->bitset_len = 0; + log_modules->bitset = g_malloc0 (log_modules->bitset_allocated); + } + + if ((m = g_hash_table_lookup (log_modules->modules, mname)) == NULL) { + m = g_malloc0 (sizeof (*m)); + m->mname = g_strdup (mname); + m->id = rspamd_logger_allocate_mod_bit (); + clrbit (log_modules->bitset, m->id); + g_hash_table_insert (log_modules->modules, m->mname, m); + } + + return m->id; +} + +void +rspamd_logger_configure_modules (GHashTable *mods_enabled) +{ + GHashTableIter it; + gpointer k, v; + guint id; + + /* On first iteration, we go through all modules enabled and add missing ones */ + g_hash_table_iter_init (&it, mods_enabled); + + while (g_hash_table_iter_next (&it, &k, &v)) { + rspamd_logger_add_debug_module ((const gchar *)k); + } + + g_hash_table_iter_init (&it, mods_enabled); + + while (g_hash_table_iter_next (&it, &k, &v)) { + id = rspamd_logger_add_debug_module ((const gchar *)k); + + if (isclr (log_modules->bitset, id)) { + msg_info ("enable debugging for module %s (%d)", (const gchar *) k, + id); + setbit (log_modules->bitset, id); + } + } +} + +struct rspamd_logger_funcs* +rspamd_logger_set_log_function (rspamd_logger_t *logger, + struct rspamd_logger_funcs *nfuncs) +{ + /* TODO: write this */ + + return NULL; +}
\ No newline at end of file diff --git a/src/libserver/logger/logger_console.c b/src/libserver/logger/logger_console.c new file mode 100644 index 000000000..1bc91007a --- /dev/null +++ b/src/libserver/logger/logger_console.c @@ -0,0 +1,294 @@ +/*- + * Copyright 2020 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "logger.h" +#include "libserver/cfg_file.h" +#include "libcryptobox/cryptobox.h" +#include "unix-std.h" + +#include "logger_private.h" + +#define CONSOLE_LOG_QUARK g_quark_from_static_string ("console_logger") + +static const gchar lf_chr = '\n'; +struct rspamd_console_logger_priv { + gint fd; + gint crit_fd; + gboolean log_color; + gboolean log_rspamadm; + gboolean log_tty; +}; + +/* Copy & paste :( */ +static inline void +log_time (gdouble now, rspamd_logger_t *rspamd_log, gchar *timebuf, + size_t len) +{ + time_t sec = (time_t)now; + gsize r; + struct tm tms; + + rspamd_localtime (sec, &tms); + r = strftime (timebuf, len, "%F %H:%M:%S", &tms); + + if (rspamd_log->flags & RSPAMD_LOG_FLAG_USEC) { + gchar usec_buf[16]; + + rspamd_snprintf (usec_buf, sizeof (usec_buf), "%.5f", + now - (gdouble)sec); + rspamd_snprintf (timebuf + r, len - r, + "%s", usec_buf + 1); + } +} + +void * +rspamd_log_console_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err) +{ + struct rspamd_console_logger_priv *priv; + + priv = g_malloc0 (sizeof (*priv)); + priv->log_color = (logger->flags & RSPAMD_LOG_FLAG_COLOR); + priv->log_rspamadm = (logger->flags & RSPAMD_LOG_FLAG_RSPAMADM); + + if (priv->log_rspamadm) { + priv->fd = dup (STDOUT_FILENO); + priv->crit_fd = dup (STDERR_FILENO); + } + else { + priv->fd = dup (STDERR_FILENO); + priv->crit_fd = priv->fd; + } + + if (priv->fd == -1) { + g_set_error (err, CONSOLE_LOG_QUARK, errno, + "open_log: cannot dup console fd: %s\n", + strerror (errno)); + rspamd_log_console_dtor (logger, priv); + + return NULL; + } + + if (isatty (priv->fd)) { + priv->log_tty = true; + } + else if (priv->log_color) { + /* Disable colors for not a tty */ + priv->log_color = false; + } + + return priv; +} + +void * +rspamd_log_console_reload (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, uid_t uid, gid_t gid, GError **err) +{ + struct rspamd_console_logger_priv *npriv; + + npriv = rspamd_log_console_init (logger, cfg, uid, gid, err); + + if (npriv) { + /* Close old */ + rspamd_log_console_dtor (logger, arg); + } + + return npriv; +} + +void +rspamd_log_console_dtor (rspamd_logger_t *logger, gpointer arg) +{ + struct rspamd_console_logger_priv *priv = (struct rspamd_console_logger_priv *)arg; + + if (priv->fd != -1) { + if (priv->fd != priv->crit_fd) { + /* Two different FD case */ + if (close (priv->crit_fd) == -1) { + rspamd_fprintf (stderr, "cannot close log crit_fd %d: %s\n", + priv->crit_fd, strerror (errno)); + } + } + + if (close (priv->fd) == -1) { + rspamd_fprintf (stderr, "cannot close log fd %d: %s\n", + priv->fd, strerror (errno)); + } + + /* Avoid the next if to be executed as crit_fd is equal to fd */ + priv->crit_fd = -1; + } + + if (priv->crit_fd != -1) { + if (close (priv->crit_fd) == -1) { + rspamd_fprintf (stderr, "cannot close log crit_fd %d: %s\n", + priv->crit_fd, strerror (errno)); + } + } + + g_free (priv); +} + +bool +rspamd_log_console_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg) +{ + struct rspamd_console_logger_priv *priv = (struct rspamd_console_logger_priv *)arg; + static gchar timebuf[64], modulebuf[64]; + gchar tmpbuf[256]; + gchar *m; + struct iovec iov[6]; + gulong r = 0, mr = 0; + size_t mremain; + gint fd, niov = 0; + + if (level_flags & G_LOG_LEVEL_CRITICAL) { + fd = priv->crit_fd; + } + else { + fd = priv->fd; + } + +#ifndef DISABLE_PTHREAD_MUTEX + if (rspamd_log->mtx) { + rspamd_mempool_lock_mutex (rspamd_log->mtx); + } + else { + rspamd_file_lock (fd, FALSE); + } +#else + rspamd_file_lock (fd, FALSE); +#endif + + log_time (rspamd_get_calendar_ticks (), + rspamd_log, timebuf, sizeof (timebuf)); + if (priv->log_color) { + if (level_flags & (G_LOG_LEVEL_INFO|G_LOG_LEVEL_MESSAGE)) { + /* White */ + r = rspamd_snprintf (tmpbuf, sizeof (tmpbuf), "\033[0;37m"); + } + else if (level_flags & G_LOG_LEVEL_WARNING) { + /* Magenta */ + r = rspamd_snprintf (tmpbuf, sizeof (tmpbuf), "\033[0;32m"); + } + else if (level_flags & G_LOG_LEVEL_CRITICAL) { + /* Red */ + r = rspamd_snprintf (tmpbuf, sizeof (tmpbuf), "\033[1;31m"); + } + } + else { + r = 0; + } + + if (priv->log_rspamadm) { + if (rspamd_log->log_level == G_LOG_LEVEL_DEBUG) { + log_time (rspamd_get_calendar_ticks (), + rspamd_log, timebuf, sizeof (timebuf)); + iov[niov].iov_base = (void *) timebuf; + iov[niov++].iov_len = strlen (timebuf); + iov[niov].iov_base = (void *) " "; + iov[niov++].iov_len = 1; + } + + iov[niov].iov_base = (void *) message; + iov[niov++].iov_len = mlen; + iov[niov].iov_base = (void *) &lf_chr; + iov[niov++].iov_len = 1; + } + else { + r += rspamd_snprintf (tmpbuf + r, + sizeof (tmpbuf) - r, + "%s #%P(%s) ", + timebuf, + rspamd_log->pid, + rspamd_log->process_type); + + modulebuf[0] = '\0'; + mremain = sizeof (modulebuf); + m = modulebuf; + + if (id != NULL) { + guint slen = strlen (id); + slen = MIN (LOG_ID, slen); + mr = rspamd_snprintf (m, mremain, "<%*.s>; ", slen, + id); + m += mr; + mremain -= mr; + } + if (module != NULL) { + mr = rspamd_snprintf (m, mremain, "%s; ", module); + m += mr; + mremain -= mr; + } + if (function != NULL) { + mr = rspamd_snprintf (m, mremain, "%s: ", function); + m += mr; + mremain -= mr; + } + else { + mr = rspamd_snprintf (m, mremain, ": "); + m += mr; + mremain -= mr; + } + + iov[niov].iov_base = tmpbuf; + iov[niov++].iov_len = r; + iov[niov].iov_base = modulebuf; + iov[niov++].iov_len = m - modulebuf; + iov[niov].iov_base = (void *) message; + iov[niov++].iov_len = mlen; + iov[niov].iov_base = (void *) &lf_chr; + iov[niov++].iov_len = 1; + } + + if (priv->log_color) { + iov[niov].iov_base = "\033[0m"; + iov[niov++].iov_len = sizeof ("\033[0m") - 1; + } + +again: + r = writev (fd, iov, niov); + + if (r == -1) { + if (errno == EAGAIN || errno == EINTR) { + goto again; + } + + if (rspamd_log->mtx) { + rspamd_mempool_unlock_mutex (rspamd_log->mtx); + } + else { + rspamd_file_unlock (fd, FALSE); + } + + return false; + } + + if (rspamd_log->mtx) { + rspamd_mempool_unlock_mutex (rspamd_log->mtx); + } + else { + rspamd_file_unlock (fd, FALSE); + } + + return true; +}
\ No newline at end of file diff --git a/src/libserver/logger/logger_file.c b/src/libserver/logger/logger_file.c new file mode 100644 index 000000000..9a242ac61 --- /dev/null +++ b/src/libserver/logger/logger_file.c @@ -0,0 +1,597 @@ +/*- + * Copyright 2020 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "logger.h" +#include "libserver/cfg_file.h" +#include "libcryptobox/cryptobox.h" +#include "unix-std.h" + +#include "logger_private.h" + +#define FILE_LOG_QUARK g_quark_from_static_string ("file_logger") + +static const gchar lf_chr = '\n'; + +struct rspamd_file_logger_priv { + gint fd; + struct { + guint32 size; + guint32 used; + u_char *buf; + } io_buf; + gboolean throttling; + gchar *log_file; + gboolean is_buffered; + time_t throttling_time; + guint32 repeats; + guint64 last_line_cksum; + gchar *saved_message; + gsize saved_mlen; + gchar *saved_function; + gchar *saved_module; + gchar *saved_id; + guint saved_loglevel; +}; + +/** + * Calculate checksum for log line (used for repeating logic) + */ +static inline guint64 +rspamd_log_calculate_cksum (const gchar *message, size_t mlen) +{ + return rspamd_cryptobox_fast_hash (message, mlen, rspamd_hash_seed ()); +} + +static inline void +log_time (gdouble now, rspamd_logger_t *rspamd_log, gchar *timebuf, + size_t len) +{ + time_t sec = (time_t)now; + gsize r; + struct tm tms; + + rspamd_localtime (sec, &tms); + r = strftime (timebuf, len, "%F %H:%M:%S", &tms); + + if (rspamd_log->flags & RSPAMD_LOG_FLAG_USEC) { + gchar usec_buf[16]; + + rspamd_snprintf (usec_buf, sizeof (usec_buf), "%.5f", + now - (gdouble)sec); + rspamd_snprintf (timebuf + r, len - r, + "%s", usec_buf + 1); + } +} + + +/* + * Write a line to log file (unbuffered) + */ +static bool +direct_write_log_line (rspamd_logger_t *rspamd_log, + struct rspamd_file_logger_priv *priv, + void *data, + gsize count, + gboolean is_iov, + gint level_flags) +{ + gchar errmsg[128]; + struct iovec *iov; + const gchar *line; + glong r; + gint fd; + gboolean locked = FALSE; + + iov = (struct iovec *) data; + fd = priv->fd; + + if (!rspamd_log->no_lock) { + gsize tlen; + + if (is_iov) { + tlen = 0; + + for (guint i = 0; i < count; i ++) { + tlen += iov[i].iov_len; + } + } + else { + tlen = count; + } + + if (tlen > PIPE_BUF) { + locked = TRUE; + +#ifndef DISABLE_PTHREAD_MUTEX + if (rspamd_log->mtx) { + rspamd_mempool_lock_mutex (rspamd_log->mtx); + } + else { + rspamd_file_lock (fd, FALSE); + } +#else + rspamd_file_lock (fd, FALSE); +#endif + } + } + + if (is_iov) { + r = writev (fd, iov, count); + } + else { + line = (const gchar *) data; + r = write (fd, line, count); + } + + if (locked) { +#ifndef DISABLE_PTHREAD_MUTEX + if (rspamd_log->mtx) { + rspamd_mempool_unlock_mutex (rspamd_log->mtx); + } + else { + rspamd_file_unlock (fd, FALSE); + } +#else + rspamd_file_unlock (fd, FALSE); +#endif + } + + if (r == -1) { + /* We cannot write message to file, so we need to detect error and make decision */ + if (errno == EINTR) { + /* Try again */ + return direct_write_log_line (rspamd_log, priv, data, count, is_iov, level_flags); + } + + if (errno == EFAULT || errno == EINVAL || errno == EFBIG || + errno == ENOSPC) { + /* Rare case */ + priv->throttling = TRUE; + priv->throttling_time = time (NULL); + } + else if (errno == EPIPE || errno == EBADF) { + /* We write to some pipe and it disappears, disable logging or we has opened bad file descriptor */ + rspamd_log->enabled = FALSE; + } + + return false; + } + else if (priv->throttling) { + priv->throttling = FALSE; + } + + return true; +} + +/** + * Fill buffer with message (limits must be checked BEFORE this call) + */ +static void +fill_buffer (rspamd_logger_t *rspamd_log, + struct rspamd_file_logger_priv *priv, + const struct iovec *iov, gint iovcnt) +{ + gint i; + + for (i = 0; i < iovcnt; i++) { + memcpy (priv->io_buf.buf + priv->io_buf.used, + iov[i].iov_base, + iov[i].iov_len); + priv->io_buf.used += iov[i].iov_len; + } + +} + +static void +rspamd_log_flush (rspamd_logger_t *rspamd_log, struct rspamd_file_logger_priv *priv) +{ + if (priv->is_buffered) { + direct_write_log_line (rspamd_log, + priv, + priv->io_buf.buf, + priv->io_buf.used, + FALSE, + rspamd_log->log_level); + priv->io_buf.used = 0; + } +} + +/* + * Write message to buffer or to file (using direct_write_log_line function) + */ +static bool +file_log_helper (rspamd_logger_t *rspamd_log, + struct rspamd_file_logger_priv *priv, + const struct iovec *iov, + guint iovcnt, + gint level_flags) +{ + size_t len = 0; + guint i; + + if (!priv->is_buffered) { + /* Write string directly */ + return direct_write_log_line (rspamd_log, priv, (void *) iov, iovcnt, + TRUE, level_flags); + } + else { + /* Calculate total length */ + for (i = 0; i < iovcnt; i++) { + len += iov[i].iov_len; + } + /* Fill buffer */ + if (priv->io_buf.size < len) { + /* Buffer is too small to hold this string, so write it directly */ + rspamd_log_flush (rspamd_log, priv); + return direct_write_log_line (rspamd_log, priv, (void *) iov, iovcnt, + TRUE, level_flags); + } + else if (priv->io_buf.used + len >= priv->io_buf.size) { + /* Buffer is full, try to write it directly */ + rspamd_log_flush (rspamd_log, priv); + fill_buffer (rspamd_log, priv, iov, iovcnt); + } + else { + /* Copy incoming string to buffer */ + fill_buffer (rspamd_log, priv, iov, iovcnt); + } + } + + return true; +} + +static void +rspamd_log_reset_repeated (rspamd_logger_t *rspamd_log, + struct rspamd_file_logger_priv *priv) +{ + gchar tmpbuf[256]; + gssize r; + + if (priv->repeats > REPEATS_MIN) { + r = rspamd_snprintf (tmpbuf, + sizeof (tmpbuf), + "Last message repeated %ud times", + priv->repeats - REPEATS_MIN); + priv->repeats = 0; + + if (priv->saved_message) { + rspamd_log_file_log (priv->saved_module, + priv->saved_id, + priv->saved_function, + priv->saved_loglevel | RSPAMD_LOG_FORCED, + priv->saved_message, + priv->saved_mlen, + rspamd_log, + priv); + + g_free (priv->saved_message); + g_free (priv->saved_function); + g_free (priv->saved_module); + g_free (priv->saved_id); + priv->saved_message = NULL; + priv->saved_function = NULL; + priv->saved_module = NULL; + priv->saved_id = NULL; + } + + /* It is safe to use temporary buffer here as it is not static */ + rspamd_log_file_log (NULL, NULL, + G_STRFUNC, + priv->saved_loglevel | RSPAMD_LOG_FORCED, + tmpbuf, + r, + rspamd_log, + priv); + rspamd_log_flush (rspamd_log, priv); + } +} + +static gint +rspamd_try_open_log_fd (rspamd_logger_t *rspamd_log, + struct rspamd_file_logger_priv *priv, + uid_t uid, gid_t gid, + GError **err) +{ + gint fd; + + fd = open (priv->log_file, + O_CREAT | O_WRONLY | O_APPEND, + S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); + if (fd == -1) { + g_set_error (err, FILE_LOG_QUARK, errno, + "open_log: cannot open desired log file: %s, %s\n", + priv->log_file, strerror (errno)); + return -1; + } + + if (uid != -1 || gid != -1) { + if (fchown (fd, uid, gid) == -1) { + g_set_error (err, FILE_LOG_QUARK, errno, + "open_log: cannot chown desired log file: %s, %s\n", + priv->log_file, strerror (errno)); + close (fd); + + return -1; + } + } + + return fd; +} + +void * +rspamd_log_file_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err) +{ + struct rspamd_file_logger_priv *priv; + + if (!cfg || !cfg->cfg_name) { + g_set_error (err, FILE_LOG_QUARK, EINVAL, + "no log file specified"); + return NULL; + } + + priv = g_malloc0 (sizeof (*priv)); + + if (cfg->log_buffered) { + if (cfg->log_buf_size != 0) { + priv->io_buf.size = cfg->log_buf_size; + } + else { + priv->io_buf.size = LOGBUF_LEN; + } + priv->is_buffered = TRUE; + priv->io_buf.buf = g_malloc (priv->io_buf.size); + } + + if (cfg->log_file) { + priv->log_file = g_strdup (cfg->log_file); + } + + priv->fd = rspamd_try_open_log_fd (logger, priv, uid, gid, err); + + if (priv->fd == -1) { + rspamd_log_file_dtor (logger, priv); + + return NULL; + } + + return priv; +} + +void +rspamd_log_file_dtor (rspamd_logger_t *logger, gpointer arg) +{ + struct rspamd_file_logger_priv *priv = (struct rspamd_file_logger_priv *)arg; + + rspamd_log_reset_repeated (logger, priv); + rspamd_log_flush (logger, priv); + + if (priv->fd != -1) { + if (close (priv->fd) == -1) { + rspamd_fprintf (stderr, "cannot close log fd %d: %s; log file = %s\n", + priv->fd, strerror (errno), priv->log_file); + } + } + + g_free (priv->log_file); + g_free (priv); +} + +bool +rspamd_log_file_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg) +{ + struct rspamd_file_logger_priv *priv = (struct rspamd_file_logger_priv *)arg; + static gchar timebuf[64], modulebuf[64]; + gchar tmpbuf[256]; + gchar *m; + gdouble now; + struct iovec iov[6]; + gulong r = 0, mr = 0; + guint64 cksum; + size_t mremain; + const gchar *cptype = NULL; + gboolean got_time = FALSE; + + + if (!(level_flags & RSPAMD_LOG_FORCED) && !rspamd_log->enabled) { + return false; + } + + /* Check throttling due to write errors */ + if (!(level_flags & RSPAMD_LOG_FORCED) && priv->throttling) { + now = rspamd_get_calendar_ticks (); + + if (priv->throttling_time != now) { + priv->throttling_time = now; + got_time = TRUE; + } + else { + /* Do not try to write to file too often while throttling */ + return false; + } + } + + /* Check repeats */ + cksum = rspamd_log_calculate_cksum (message, mlen); + + if (cksum == priv->last_line_cksum) { + priv->repeats++; + + if (priv->repeats > REPEATS_MIN && priv->repeats < + REPEATS_MAX) { + /* Do not log anything but save message for future */ + if (priv->saved_message == NULL) { + priv->saved_function = g_strdup (function); + priv->saved_mlen = mlen; + priv->saved_message = g_malloc (mlen); + memcpy (priv->saved_message, message, mlen); + + if (module) { + priv->saved_module = g_strdup (module); + } + + if (id) { + priv->saved_id = g_strdup (id); + } + + priv->saved_loglevel = level_flags; + } + + return true; + } + else if (priv->repeats > REPEATS_MAX) { + rspamd_log_reset_repeated (rspamd_log, priv); + + bool ret = rspamd_log_file_log (module, id, + function, + level_flags, + message, + mlen, + rspamd_log, + priv); + + /* Probably we have more repeats in future */ + priv->repeats = REPEATS_MIN + 1; + + return ret; + } + } + else { + /* Reset counter if new message differs from saved message */ + priv->last_line_cksum = cksum; + + if (priv->repeats > REPEATS_MIN) { + rspamd_log_reset_repeated (rspamd_log, priv); + return rspamd_log_file_log (module, id, + function, + level_flags, + message, + mlen, + rspamd_log, + arg); + } + else { + priv->repeats = 0; + } + } + if (!got_time) { + now = rspamd_get_calendar_ticks (); + } + + /* Format time */ + if (!(rspamd_log->flags & RSPAMD_LOG_FLAG_SYSTEMD)) { + log_time (now, rspamd_log, timebuf, sizeof (timebuf)); + } + + cptype = rspamd_log->process_type; + r = 0; + + if (!(rspamd_log->flags & RSPAMD_LOG_FLAG_SYSTEMD)) { + r += rspamd_snprintf (tmpbuf + r, + sizeof (tmpbuf) - r, + "%s #%P(%s) ", + timebuf, + rspamd_log->pid, + cptype); + } + else { + r += rspamd_snprintf (tmpbuf + r, + sizeof (tmpbuf) - r, + "(%s) ", + cptype); + } + + modulebuf[0] = '\0'; + mremain = sizeof (modulebuf); + m = modulebuf; + + if (id != NULL) { + guint slen = strlen (id); + slen = MIN (LOG_ID, slen); + mr = rspamd_snprintf (m, mremain, "<%*.s>; ", slen, + id); + m += mr; + mremain -= mr; + } + if (module != NULL) { + mr = rspamd_snprintf (m, mremain, "%s; ", module); + m += mr; + mremain -= mr; + } + if (function != NULL) { + mr = rspamd_snprintf (m, mremain, "%s: ", function); + m += mr; + mremain -= mr; + } + else { + mr = rspamd_snprintf (m, mremain, ": "); + m += mr; + mremain -= mr; + } + + /* Construct IOV for log line */ + iov[0].iov_base = tmpbuf; + iov[0].iov_len = r; + iov[1].iov_base = modulebuf; + iov[1].iov_len = m - modulebuf; + iov[2].iov_base = (void *) message; + iov[2].iov_len = mlen; + iov[3].iov_base = (void *) &lf_chr; + iov[3].iov_len = 1; + + return file_log_helper (rspamd_log, priv, iov, 4, level_flags); +} + +void * +rspamd_log_file_reload (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, uid_t uid, gid_t gid, GError **err) +{ + struct rspamd_file_logger_priv *npriv; + + if (!cfg->cfg_name) { + g_set_error (err, FILE_LOG_QUARK, EINVAL, + "no log file specified"); + return NULL; + } + + npriv = rspamd_log_file_init (logger, cfg, uid, gid, err); + + if (npriv) { + /* Close old */ + rspamd_log_file_dtor (logger, arg); + } + + return npriv; +} + +bool +rspamd_log_file_on_fork (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, GError **err) +{ + struct rspamd_file_logger_priv *priv = (struct rspamd_file_logger_priv *)arg; + + rspamd_log_reset_repeated (logger, priv); + rspamd_log_flush (logger, priv); + + return true; +}
\ No newline at end of file diff --git a/src/libserver/logger/logger_private.h b/src/libserver/logger/logger_private.h new file mode 100644 index 000000000..23361e38d --- /dev/null +++ b/src/libserver/logger/logger_private.h @@ -0,0 +1,161 @@ +/*- + * Copyright 2020 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef RSPAMD_LOGGER_PRIVATE_H +#define RSPAMD_LOGGER_PRIVATE_H + +/* How much message should be repeated before it is count to be repeated one */ +#define REPEATS_MIN 3 +#define REPEATS_MAX 300 +#define LOG_ID 6 +#define LOGBUF_LEN 8192 + +struct rspamd_log_module { + gchar *mname; + guint id; +}; + +struct rspamd_log_modules { + guchar *bitset; + guint bitset_len; /* Number of BITS used in bitset */ + guint bitset_allocated; /* Size of bitset allocated in BYTES */ + GHashTable *modules; +}; + +struct rspamd_logger_error_elt { + gint completed; + GQuark ptype; + pid_t pid; + gdouble ts; + gchar id[LOG_ID + 1]; + gchar module[9]; + gchar message[]; +}; + +struct rspamd_logger_error_log { + struct rspamd_logger_error_elt *elts; + rspamd_mempool_t *pool; + guint32 max_elts; + guint32 elt_len; + /* Avoid false cache sharing */ + guchar __padding[64 - sizeof(gpointer) * 2 - sizeof(guint64)]; + guint cur_row; +}; + +/** + * Static structure that store logging parameters + * It is NOT shared between processes and is created by main process + */ +struct rspamd_logger_s { + struct rspamd_logger_funcs ops; + gint log_level; + + struct rspamd_logger_error_log *errlog; + struct rspamd_cryptobox_pubkey *pk; + struct rspamd_cryptobox_keypair *keypair; + + guint flags; + gboolean closed; + gboolean enabled; + gboolean is_debug; + gboolean no_lock; + + pid_t pid; + const gchar *process_type; + struct rspamd_radix_map_helper *debug_ip; + rspamd_mempool_mutex_t *mtx; + rspamd_mempool_t *pool; + guint64 log_cnt[4]; +}; + +/* + * Common logging prototypes + */ + +/* + * File logging + */ +void * rspamd_log_file_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err); +void * rspamd_log_file_reload (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, uid_t uid, gid_t gid, GError **err); +void rspamd_log_file_dtor (rspamd_logger_t *logger, gpointer arg); +bool rspamd_log_file_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg); +bool rspamd_log_file_on_fork (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, GError **err); + +const static struct rspamd_logger_funcs file_log_funcs = { + .init = rspamd_log_file_init, + .dtor = rspamd_log_file_dtor, + .reload = rspamd_log_file_reload, + .log = rspamd_log_file_log, + .on_fork = rspamd_log_file_on_fork, +}; + +/* + * Syslog logging + */ +void * rspamd_log_syslog_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err); +void * rspamd_log_syslog_reload (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, uid_t uid, gid_t gid, GError **err); +void rspamd_log_syslog_dtor (rspamd_logger_t *logger, gpointer arg); +bool rspamd_log_syslog_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg); + +const static struct rspamd_logger_funcs syslog_log_funcs = { + .init = rspamd_log_syslog_init, + .dtor = rspamd_log_syslog_dtor, + .reload = rspamd_log_syslog_reload, + .log = rspamd_log_syslog_log, + .on_fork = NULL, +}; + +/* + * Console logging + */ +void * rspamd_log_console_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err); +void * rspamd_log_console_reload (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, uid_t uid, gid_t gid, GError **err); +void rspamd_log_console_dtor (rspamd_logger_t *logger, gpointer arg); +bool rspamd_log_console_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg); + +const static struct rspamd_logger_funcs console_log_funcs = { + .init = rspamd_log_console_init, + .dtor = rspamd_log_console_dtor, + .reload = rspamd_log_console_reload, + .log = rspamd_log_console_log, + .on_fork = NULL, +}; + +#endif diff --git a/src/libserver/logger/logger_syslog.c b/src/libserver/logger/logger_syslog.c new file mode 100644 index 000000000..5a295b288 --- /dev/null +++ b/src/libserver/logger/logger_syslog.c @@ -0,0 +1,148 @@ +/*- + * Copyright 2020 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "logger.h" +#include "libserver/cfg_file.h" +#include "logger_private.h" + +#define SYSLOG_LOG_QUARK g_quark_from_static_string ("syslog_logger") + +struct rspamd_syslog_logger_priv { + gint log_facility; +}; + +#ifdef HAVE_SYSLOG_H +#include <syslog.h> + +void * +rspamd_log_syslog_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err) +{ + struct rspamd_syslog_logger_priv *priv; + + if (!cfg) { + g_set_error (err, SYSLOG_LOG_QUARK, EINVAL, + "no log config specified"); + return NULL; + } + + priv = g_malloc0 (sizeof (*priv)); + + priv->log_facility = cfg->log_facility; + openlog ("rspamd", LOG_NDELAY | LOG_PID, priv->log_facility); + + return priv; +} + +void +rspamd_log_syslog_dtor (rspamd_logger_t *logger, gpointer arg) +{ + struct rspamd_syslog_logger_priv *priv = (struct rspamd_syslog_logger_priv *)arg; + + closelog (); + g_free (priv); +} +bool +rspamd_log_syslog_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg) +{ + static const struct { + GLogLevelFlags glib_level; + gint syslog_level; + } levels_match[] = { + {G_LOG_LEVEL_DEBUG, LOG_DEBUG}, + {G_LOG_LEVEL_INFO, LOG_INFO}, + {G_LOG_LEVEL_WARNING, LOG_WARNING}, + {G_LOG_LEVEL_CRITICAL, LOG_ERR} + }; + unsigned i; + gint syslog_level; + + if (!(level_flags & RSPAMD_LOG_FORCED) && !rspamd_log->enabled) { + return false; + } + + /* Detect level */ + syslog_level = LOG_DEBUG; + + for (i = 0; i < G_N_ELEMENTS (levels_match); i ++) { + if (level_flags & levels_match[i].glib_level) { + syslog_level = levels_match[i].syslog_level; + break; + } + } + + syslog (syslog_level, "<%.*s>; %s; %s: %*.s", + LOG_ID, id != NULL ? id : "", + module != NULL ? module : "", + function != NULL ? function : "", + (gint)mlen, message); + + return true; +} + +#else + +void * +rspamd_log_syslog_init (rspamd_logger_t *logger, struct rspamd_config *cfg, + uid_t uid, gid_t gid, GError **err) +{ + g_set_error (err, SYSLOG_LOG_QUARK, EINVAL, "syslog support is not compiled in"); + + return NULL; +} + +bool +rspamd_log_syslog_log (const gchar *module, const gchar *id, + const gchar *function, + gint level_flags, + const gchar *message, + gsize mlen, + rspamd_logger_t *rspamd_log, + gpointer arg) +{ + return false; +} + +void +rspamd_log_syslog_dtor (rspamd_logger_t *logger, gpointer arg) +{ + /* Left blank intentionally */ +} + +#endif + +void * +rspamd_log_syslog_reload (rspamd_logger_t *logger, struct rspamd_config *cfg, + gpointer arg, uid_t uid, gid_t gid, GError **err) +{ + struct rspamd_syslog_logger_priv *npriv; + + npriv = rspamd_log_syslog_init (logger, cfg, uid, gid, err); + + if (npriv) { + /* Close old */ + rspamd_log_syslog_dtor (logger, arg); + } + + return npriv; +} |