/* * 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 "util.h" #include "main.h" #include "message.h" #include "symbols_cache.h" #include "view.h" #include "cfg_file.h" #define WEIGHT_MULT 4.0 #define FREQUENCY_MULT 10.0 #define TIME_MULT -1.0 /* After which number of messages try to resort cache */ #define MAX_USES 100 /* * Symbols cache utility functions */ #define MIN_CACHE 17 static guint64 total_frequency = 0; static guint32 nsymbols = 0; gint cache_cmp (const void *p1, const void *p2) { const struct cache_item *i1 = p1, *i2 = p2; return strcmp (i1->s->symbol, i2->s->symbol); } gint cache_logic_cmp (const void *p1, const void *p2) { const struct cache_item *i1 = p1, *i2 = p2; double w1, w2; double weight1, weight2; double f1 = 0, f2 = 0; if (i1->priority == 0 && i2->priority == 0) { if (total_frequency > 0) { f1 = ((double)i1->s->frequency * nsymbols) / (double)total_frequency; f2 = ((double)i2->s->frequency * nsymbols) / (double)total_frequency; } weight1 = i1->metric_weight == 0 ? i1->s->weight : i1->metric_weight; weight2 = i2->metric_weight == 0 ? i2->s->weight : i2->metric_weight; w1 = abs (weight1) * WEIGHT_MULT + f1 * FREQUENCY_MULT + i1->s->avg_time * TIME_MULT; w2 = abs (weight2) * WEIGHT_MULT + f2 * FREQUENCY_MULT + i2->s->avg_time * TIME_MULT; } else { /* Strict sorting */ w1 = abs (i1->priority); w2 = abs (i2->priority); } return (gint)w2 - w1; } static GChecksum * get_mem_cksum (struct symbols_cache *cache) { GChecksum *result; GList *cur, *l; struct cache_item *item; result = g_checksum_new (G_CHECKSUM_SHA1); l = g_list_copy (cache->negative_items); l = g_list_sort (l, cache_cmp); cur = g_list_first (l); while (cur) { item = cur->data; if (item->s->symbol[0] != '\0') { g_checksum_update (result, item->s->symbol, strlen (item->s->symbol)); } cur = g_list_next (cur); } g_list_free (l); l = g_list_copy (cache->static_items); l = g_list_sort (l, cache_cmp); cur = g_list_first (l); while (cur) { item = cur->data; if (item->s->symbol[0] != '\0') { g_checksum_update (result, item->s->symbol, strlen (item->s->symbol)); } total_frequency += item->s->frequency; cur = g_list_next (cur); } g_list_free (l); return result; } /* Sort items in logical order */ static void post_cache_init (struct symbols_cache *cache) { GList *cur; struct cache_item *item; total_frequency = 0; nsymbols = cache->used_items; cur = g_list_first (cache->negative_items); while (cur) { item = cur->data; total_frequency += item->s->frequency; cur = g_list_next (cur); } cur = g_list_first (cache->static_items); while (cur) { item = cur->data; total_frequency += item->s->frequency; cur = g_list_next (cur); } cache->negative_items = g_list_sort (cache->negative_items, cache_logic_cmp); cache->static_items = g_list_sort (cache->static_items, cache_logic_cmp); } /* Unmap cache file */ static void unmap_cache_file (gpointer arg) { struct symbols_cache *cache = arg; /* A bit ugly usage */ munmap (cache->map, cache->used_items * sizeof (struct saved_cache_item)); } static gboolean mmap_cache_file (struct symbols_cache *cache, gint fd, memory_pool_t *pool) { guint8 *map; gint i; GList *cur; struct cache_item *item; if (cache->used_items > 0) { map = mmap (NULL, cache->used_items * sizeof (struct saved_cache_item), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { msg_err ("cannot mmap cache file: %d, %s", errno, strerror (errno)); close (fd); return FALSE; } /* Close descriptor as it would never be used */ close (fd); cache->map = map; /* Now free old values for saved cache items and fill them with mmapped ones */ i = 0; cur = g_list_first (cache->negative_items); while (cur) { item = cur->data; item->s = (struct saved_cache_item *)(map + i * sizeof (struct saved_cache_item)); cur = g_list_next (cur); i ++; } cur = g_list_first (cache->static_items); while (cur) { item = cur->data; item->s = (struct saved_cache_item *)(map + i * sizeof (struct saved_cache_item)); cur = g_list_next (cur); i ++; } post_cache_init (cache); } return TRUE; } /* Fd must be opened for writing, after creating file is mmapped */ static gboolean create_cache_file (struct symbols_cache *cache, const gchar *filename, gint fd, memory_pool_t *pool) { GChecksum *cksum; u_char *digest; gsize cklen; GList *cur; struct cache_item *item; /* Calculate checksum */ cksum = get_mem_cksum (cache); if (cksum == NULL) { msg_err ("cannot calculate checksum for symbols"); close (fd); return FALSE; } cklen = g_checksum_type_get_length (G_CHECKSUM_SHA1); digest = g_malloc (cklen); g_checksum_get_digest (cksum, digest, &cklen); /* Now write data to file */ cur = g_list_first (cache->negative_items); while (cur) { item = cur->data; if (write (fd, item->s, sizeof (struct saved_cache_item)) == -1) { msg_err ("cannot write to file %d, %s", errno, strerror (errno)); close (fd); g_checksum_free (cksum); g_free (digest); return FALSE; } cur = g_list_next (cur); } cur = g_list_first (cache->static_items); while (cur) { item = cur->data; if (write (fd, item->s, sizeof (struct saved_cache_item)) == -1) { msg_err ("cannot write to file %d, %s", errno, strerror (errno)); close (fd); g_checksum_free (cksum); g_free (digest); return FALSE; } cur = g_list_next (cur); } /* Write checksum */ if (write (fd, digest, cklen) == -1) { msg_err ("cannot write to file %d, %s", errno, strerror (errno)); close (fd); g_checksum_free (cksum); g_free (digest); return FALSE; } close (fd); g_checksum_free (cksum); g_free (digest); /* Reopen for reading */ if ((fd = open (filename, O_RDWR)) == -1) { msg_info ("cannot open file %s, error %d, %s", errno, strerror (errno)); return FALSE; } return mmap_cache_file (cache, fd, pool); } enum rspamd_symbol_type { SYMBOL_TYPE_NORMAL, SYMBOL_TYPE_VIRTUAL, SYMBOL_TYPE_CALLBACK }; static void register_symbol_common (struct symbols_cache **cache, const gchar *name, double weight, gint priority, symbol_func_t func, gpointer user_data, enum rspamd_symbol_type type) { struct cache_item *item = NULL; struct symbols_cache *pcache = *cache; GList **target; double *w; if (*cache == NULL) { pcache = g_new0 (struct symbols_cache, 1); *cache = pcache; pcache->static_pool = memory_pool_new (memory_pool_get_size ()); pcache->items_by_symbol = g_hash_table_new (rspamd_str_hash, rspamd_str_equal); } item = memory_pool_alloc0 (pcache->static_pool, sizeof (struct cache_item)); item->s = memory_pool_alloc0 (pcache->static_pool, sizeof (struct saved_cache_item)); rspamd_strlcpy (item->s->symbol, name, sizeof (item->s->symbol)); item->func = func; item->user_data = user_data; item->priority = priority; switch (type) { case SYMBOL_TYPE_NORMAL: break; case SYMBOL_TYPE_VIRTUAL: item->is_virtual = TRUE; break; case SYMBOL_TYPE_CALLBACK: item->is_callback = TRUE; break; } /* Handle weight using default metric */ if (pcache->cfg && pcache->cfg->default_metric && (w = g_hash_table_lookup (pcache->cfg->default_metric->symbols, name)) != NULL) { item->s->weight = weight * (*w); } else { item->s->weight = weight; } /* If we have undefined priority determine list according to weight */ if (priority == 0) { if (item->s->weight > 0) { target = &(*cache)->static_items; } else { target = &(*cache)->negative_items; } } else { /* Items with more priority are called before items with less priority */ if (priority < 0) { target = &(*cache)->negative_items; } else { target = &(*cache)->static_items; } } pcache->used_items++; g_hash_table_insert (pcache->items_by_symbol, item->s->symbol, item); msg_debug ("used items: %d, added symbol: %s", (*cache)->used_items, name); set_counter (item->s->symbol, 0); *target = g_list_prepend (*target, item); } void register_symbol (struct symbols_cache **cache, const gchar *name, double weight, symbol_func_t func, gpointer user_data) { register_symbol_common (cache, name, weight, 0, func, user_data, SYMBOL_TYPE_NORMAL); } void register_virtual_symbol (struct symbols_cache **cache, const gchar *name, double weight) { register_symbol_common (cache, name, weight, 0, NULL, NULL, SYMBOL_TYPE_VIRTUAL); } void register_callback_symbol (struct symbols_cache **cache, const gchar *name, double weight, symbol_func_t func, gpointer user_data) { register_symbol_common (cache, name, weight, 0, func, user_data, SYMBOL_TYPE_CALLBACK); } void register_callback_symbol_priority (struct symbols_cache **cache, const gchar *name, double weight, gint priority, symbol_func_t func, gpointer user_data) { register_symbol_common (cache, name, weight, priority, func, user_data, SYMBOL_TYPE_CALLBACK); } void register_dynamic_symbol (memory_pool_t *dynamic_pool, struct symbols_cache **cache, const gchar *name, double weight, symbol_func_t func, gpointer user_data, GList *networks) { struct cache_item *item = NULL; struct symbols_cache *pcache = *cache; GList *t, *cur; uintptr_t r; double *w; guint32 mask = 0xFFFFFFFF; struct dynamic_map_item *it; gint rr; if (*cache == NULL) { pcache = g_new0 (struct symbols_cache, 1); *cache = pcache; pcache->static_pool = memory_pool_new (memory_pool_get_size ()); } item = memory_pool_alloc0 (dynamic_pool, sizeof (struct cache_item)); item->s = memory_pool_alloc (dynamic_pool, sizeof (struct saved_cache_item)); rspamd_strlcpy (item->s->symbol, name, sizeof (item->s->symbol)); item->func = func; item->user_data = user_data; /* Handle weight using default metric */ if (pcache->cfg && pcache->cfg->default_metric && (w = g_hash_table_lookup (pcache->cfg->default_metric->symbols, name)) != NULL) { item->s->weight = weight * (*w); } else { item->s->weight = weight; } item->is_dynamic = TRUE; item->priority = 0; pcache->used_items++; msg_debug ("used items: %d, added symbol: %s", (*cache)->used_items, name); set_counter (item->s->symbol, 0); g_hash_table_insert (pcache->items_by_symbol, item->s->symbol, item); if (networks == NULL) { pcache->dynamic_items = g_list_prepend (pcache->dynamic_items, item); } else { if (pcache->dynamic_map == NULL) { pcache->dynamic_map = radix_tree_create (); pcache->negative_dynamic_map = radix_tree_create (); } cur = networks; while (cur) { it = cur->data; mask = mask << (32 - it->mask); r = ntohl (it->addr.s_addr & mask); if (it->negative) { /* For negatve items insert into list and into negative cache map */ if ((r = radix32tree_find (pcache->negative_dynamic_map, r)) != RADIX_NO_VALUE) { t = (GList *)((gpointer)r); t = g_list_prepend (t, item); /* Replace pointers in radix tree and in destructor function */ memory_pool_replace_destructor (dynamic_pool, (pool_destruct_func)g_list_free, (gpointer)r, t); rr = radix32tree_replace (pcache->negative_dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t); if (rr == -1) { msg_warn ("cannot replace ip to tree: %s, mask %X", inet_ntoa (it->addr), mask); } } else { t = g_list_prepend (NULL, item); memory_pool_add_destructor (dynamic_pool, (pool_destruct_func)g_list_free, t); rr = radix32tree_insert (pcache->negative_dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t); if (rr == -1) { msg_warn ("cannot insert ip to tree: %s, mask %X", inet_ntoa (it->addr), mask); } else if (rr == 1) { msg_warn ("ip %s, mask %X, value already exists", inet_ntoa (it->addr), mask); } } /* Insert into list */ pcache->dynamic_items = g_list_prepend (pcache->dynamic_items, item); } else { if ((r = radix32tree_find (pcache->dynamic_map, r)) != RADIX_NO_VALUE) { t = (GList *)((gpointer)r); t = g_list_prepend (t, item); /* Replace pointers in radix tree and in destructor function */ memory_pool_replace_destructor (dynamic_pool, (pool_destruct_func)g_list_free, (gpointer)r, t); rr = radix32tree_replace (pcache->dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t); if (rr == -1) { msg_warn ("cannot replace ip to tree: %s, mask %X", inet_ntoa (it->addr), mask); } } else { t = g_list_prepend (NULL, item); memory_pool_add_destructor (dynamic_pool, (pool_destruct_func)g_list_free, t); rr = radix32tree_insert (pcache->dynamic_map, ntohl (it->addr.s_addr), mask, (uintptr_t)t); if (rr == -1) { msg_warn ("cannot insert ip to tree: %s, mask %X", inet_ntoa (it->addr), mask); } else if (rr == 1) { msg_warn ("ip %s, mask %X, value already exists", inet_ntoa (it->addr), mask); } } } cur = g_list_next (cur); } } } void remove_dynamic_rules (struct symbols_cache *cache) { if (cache->dynamic_items) { g_list_free (cache->dynamic_items); cache->dynamic_items = NULL; } if (cache->dynamic_map) { radix_tree_free (cache->dynamic_map); cache->dynamic_map = NULL; } if (cache->negative_dynamic_map) { radix_tree_free (cache->negative_dynamic_map); cache->negative_dynamic_map = NULL; } } static void free_cache (gpointer arg) { struct symbols_cache *cache = arg; if (cache->map != NULL) { unmap_cache_file (cache); } if (cache->static_items) { g_list_free (cache->static_items); } if (cache->negative_items) { g_list_free (cache->negative_items); } if (cache->dynamic_items) { g_list_free (cache->dynamic_items); } if (cache->dynamic_map) { radix_tree_free (cache->dynamic_map); } if (cache->negative_dynamic_map) { radix_tree_free (cache->negative_dynamic_map); } g_hash_table_destroy (cache->items_by_symbol); memory_pool_delete (cache->static_pool); g_free (cache); } gboolean init_symbols_cache (memory_pool_t * pool, struct symbols_cache *cache, struct config_file *cfg, const gchar *filename, gboolean ignore_checksum) { struct stat st; gint fd; GChecksum *cksum; u_char *mem_sum, *file_sum; gsize cklen; gboolean res; if (cache == NULL) { return FALSE; } /* Init locking */ cache->lock = memory_pool_get_rwlock (pool); cache->cfg = cfg; /* Just in-memory cache */ if (filename == NULL) { post_cache_init (cache); return TRUE; } /* First of all try to stat file */ if (stat (filename, &st) == -1) { /* Check errno */ if (errno == ENOENT) { /* Try to create file */ if ((fd = open (filename, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) { msg_info ("cannot create file %s, error %d, %s", filename, errno, strerror (errno)); return FALSE; } else { return create_cache_file (cache, filename, fd, pool); } } else { msg_info ("cannot stat file %s, error %d, %s", filename, errno, strerror (errno)); return FALSE; } } else { if ((fd = open (filename, O_RDWR)) == -1) { msg_info ("cannot open file %s, error %d, %s", filename, errno, strerror (errno)); return FALSE; } } if (!ignore_checksum) { /* Calculate checksum */ cksum = get_mem_cksum (cache); if (cksum == NULL) { msg_err ("cannot calculate checksum for symbols"); close (fd); return FALSE; } cklen = g_checksum_type_get_length (G_CHECKSUM_SHA1); mem_sum = g_malloc (cklen); g_checksum_get_digest (cksum, mem_sum, &cklen); /* Now try to read file sum */ if (lseek (fd, -(cklen), SEEK_END) == -1) { if (errno == EINVAL) { /* Try to create file */ msg_info ("recreate cache file"); if ((fd = open (filename, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) { msg_info ("cannot create file %s, error %d, %s", filename, errno, strerror (errno)); return FALSE; } else { return create_cache_file (cache, filename, fd, pool); } } close (fd); g_free (mem_sum); g_checksum_free (cksum); msg_err ("cannot seek to read checksum, %d, %s", errno, strerror (errno)); return FALSE; } file_sum = g_malloc (cklen); if (read (fd, file_sum, cklen) == -1) { close (fd); g_free (mem_sum); g_free (file_sum); g_checksum_free (cksum); msg_err ("cannot read checksum, %d, %s", errno, strerror (errno)); return FALSE; } if (memcmp (file_sum, mem_sum, cklen) != 0) { close (fd); g_free (mem_sum); g_free (file_sum); g_checksum_free (cksum); msg_info ("checksum mismatch, recreating file"); /* Reopen with rw permissions */ if ((fd = open (filename, O_RDWR | O_TRUNC | O_CREAT, S_IWUSR | S_IRUSR)) == -1) { msg_info ("cannot create file %s, error %d, %s", filename, errno, strerror (errno)); return FALSE; } else { return create_cache_file (cache, filename, fd, pool); } } g_free (mem_sum); g_free (file_sum); g_checksum_free (cksum); } /* MMap cache file and copy saved_cache structures */ res = mmap_cache_file (cache, fd, pool); memory_pool_add_destructor (pool, (pool_destruct_func)free_cache, cache); return res; } static GList * check_dynamic_item (struct worker_task *task, struct symbols_cache *cache) { #ifdef HAVE_INET_PTON /* TODO: radix doesn't support ipv6 addrs */ return NULL; #else GList *res = NULL; uintptr_t r; if (cache->dynamic_map != NULL && task->from_addr.s_addr != INADDR_NONE) { if ((r = radix32tree_find (cache->dynamic_map, ntohl (task->from_addr.s_addr))) != RADIX_NO_VALUE) { res = (GList *)((gpointer)r); return res; } else { return NULL; } } return res; #endif } static gboolean check_negative_dynamic_item (struct worker_task *task, struct symbols_cache *cache, struct cache_item *item) { #ifdef HAVE_INET_PTON /* TODO: radix doesn't support ipv6 addrs */ return FALSE; #else GList *res = NULL; uintptr_t r; if (cache->negative_dynamic_map != NULL && task->from_addr.s_addr != INADDR_NONE) { if ((r = radix32tree_find (cache->negative_dynamic_map, ntohl (task->from_addr.s_addr))) != RADIX_NO_VALUE) { res = (GList *)((gpointer)r); while (res) { if (res->data == (gpointer)item) { return TRUE; } res = g_list_next (res); } } } return FALSE; #endif } static gboolean check_debug_symbol (struct config_file *cfg, const gchar *symbol) { GList *cur; cur = cfg->debug_symbols; while (cur) { if (strcmp (symbol, (const gchar *)cur->data) == 0) { return TRUE; } cur = g_list_next (cur); } return FALSE; } static void rspamd_symbols_cache_metric_cb (gpointer k, gpointer v, gpointer ud) { struct symbols_cache *cache = (struct symbols_cache *)ud; GList *cur; const gchar *sym = k; gdouble weight = *(gdouble *)v; struct cache_item *item; cur = cache->negative_items; while (cur) { item = cur->data; if (strcmp (item->s->symbol, sym) == 0) { item->metric_weight = weight; return; } cur = g_list_next (cur); } cur = cache->static_items; while (cur) { item = cur->data; if (strcmp (item->s->symbol, sym) == 0) { item->metric_weight = weight; return; } cur = g_list_next (cur); } } gboolean validate_cache (struct symbols_cache *cache, struct config_file *cfg, gboolean strict) { struct cache_item *item; GList *cur, *p, *metric_symbols; gboolean res; if (cache == NULL) { msg_err ("empty cache is invalid"); return FALSE; } /* Check each symbol in a cache and find its weight definition */ cur = cache->negative_items; while (cur) { item = cur->data; if (!item->is_callback) { if (g_hash_table_lookup (cfg->metrics_symbols, item->s->symbol) == NULL) { if (strict) { msg_warn ("no weight registered for symbol %s", item->s->symbol); return FALSE; } else { msg_info ("no weight registered for symbol %s", item->s->symbol); } } } cur = g_list_next (cur); } cur = cache->static_items; while (cur) { item = cur->data; if (!item->is_callback) { if (g_hash_table_lookup (cfg->metrics_symbols, item->s->symbol) == NULL) { if (strict) { msg_warn ("no weight registered for symbol %s", item->s->symbol); return FALSE; } else { msg_info ("no weight registered for symbol %s", item->s->symbol); } } } cur = g_list_next (cur); } #ifndef GLIB_HASH_COMPAT /* Now check each metric item and find corresponding symbol in a cache */ metric_symbols = g_hash_table_get_keys (cfg->metrics_symbols); cur = metric_symbols; while (cur) { res = FALSE; p = cache->negative_items; while (p) { item = p->data; if (strcmp (item->s->symbol, cur->data) == 0) { res = TRUE; break; } p = g_list_next (p); } if (!res) { p = cache->static_items; while (p) { item = p->data; if (strcmp (item->s->symbol, cur->data) == 0) { res = TRUE; break; } p = g_list_next (p); } } if (!res) { msg_warn ("symbol '%s' is registered in metric but not found in cache", cur->data); if (strict) { return FALSE; } } cur = g_list_next (cur); } g_list_free (metric_symbols); #endif /* GLIB_COMPAT */ /* Now adjust symbol weights according to default metric */ if (cfg->default_metric != NULL) { g_hash_table_foreach (cfg->default_metric->symbols, rspamd_symbols_cache_metric_cb, cache); /* Resort caches */ cache->negative_items = g_list_sort (cache->negative_items, cache_logic_cmp); cache->static_items = g_list_sort (cache->static_items, cache_logic_cmp); } return TRUE; } struct symbol_callback_data { enum { CACHE_STATE_NEGATIVE, CACHE_STATE_DYNAMIC_MAP, CACHE_STATE_DYNAMIC, CACHE_STATE_STATIC } state; struct cache_item *saved_item; GList *list_pointer; }; gboolean call_symbol_callback (struct worker_task * task, struct symbols_cache * cache, gpointer *save) { #ifdef HAVE_CLOCK_GETTIME struct timespec ts1, ts2; #else struct timeval tv1, tv2; #endif guint64 diff; struct cache_item *item = NULL; struct symbol_callback_data *s = *save; if (s == NULL) { if (cache == NULL) { return FALSE; } if (cache->uses++ >= MAX_USES) { msg_info ("resort symbols cache"); memory_pool_wlock_rwlock (cache->lock); cache->uses = 0; /* Resort while having write lock */ post_cache_init (cache); memory_pool_wunlock_rwlock (cache->lock); } s = memory_pool_alloc0 (task->task_pool, sizeof (struct symbol_callback_data)); *save = s; if (cache->negative_items != NULL) { s->list_pointer = g_list_first (cache->negative_items); s->saved_item = s->list_pointer->data; s->state = CACHE_STATE_NEGATIVE; } else if ((s->list_pointer = check_dynamic_item (task, cache)) || cache->dynamic_items != NULL) { if (s->list_pointer == NULL) { s->list_pointer = g_list_first (cache->dynamic_items); s->saved_item = s->list_pointer->data; s->state = CACHE_STATE_DYNAMIC; } else { s->saved_item = s->list_pointer->data; s->state = CACHE_STATE_DYNAMIC_MAP; } } else { s->state = CACHE_STATE_STATIC; s->list_pointer = g_list_first (cache->static_items); if (s->list_pointer) { s->saved_item = s->list_pointer->data; } else { return FALSE; } } item = s->saved_item; } else { if (cache == NULL) { return FALSE; } switch (s->state) { case CACHE_STATE_NEGATIVE: s->list_pointer = g_list_next (s->list_pointer); if (s->list_pointer == NULL) { if ((s->list_pointer = check_dynamic_item (task, cache)) || cache->dynamic_items != NULL) { if (s->list_pointer == NULL) { s->list_pointer = g_list_first (cache->dynamic_items); s->saved_item = s->list_pointer->data; s->state = CACHE_STATE_DYNAMIC; } else { s->saved_item = s->list_pointer->data; s->state = CACHE_STATE_DYNAMIC_MAP; } } else { s->state = CACHE_STATE_STATIC; s->list_pointer = g_list_first (cache->static_items); if (s->list_pointer) { s->saved_item = s->list_pointer->data; } else { return FALSE; } } } else { s->saved_item = s->list_pointer->data; } item = s->saved_item; break; case CACHE_STATE_DYNAMIC_MAP: s->list_pointer = g_list_next (s->list_pointer); if (s->list_pointer == NULL) { s->list_pointer = g_list_first (cache->dynamic_items); if (s->list_pointer) { s->saved_item = s->list_pointer->data; s->state = CACHE_STATE_DYNAMIC; } else { s->state = CACHE_STATE_STATIC; s->list_pointer = g_list_first (cache->static_items); if (s->list_pointer) { s->saved_item = s->list_pointer->data; } else { return FALSE; } } } else { s->saved_item = s->list_pointer->data; } item = s->saved_item; break; case CACHE_STATE_DYNAMIC: s->list_pointer = g_list_next (s->list_pointer); if (s->list_pointer == NULL) { s->state = CACHE_STATE_STATIC; s->list_pointer = g_list_first (cache->static_items); if (s->list_pointer) { s->saved_item = s->list_pointer->data; } else { return FALSE; } } else { s->saved_item = s->list_pointer->data; /* Skip items that are in negative map */ while (s->list_pointer != NULL && check_negative_dynamic_item (task, cache, s->saved_item)) { s->list_pointer = g_list_next (s->list_pointer); if (s->list_pointer != NULL) { s->saved_item = s->list_pointer->data; } } if (s->list_pointer == NULL) { s->state = CACHE_STATE_STATIC; s->list_pointer = g_list_first (cache->static_items); if (s->list_pointer) { s->saved_item = s->list_pointer->data; } else { return FALSE; } } } item = s->saved_item; break; case CACHE_STATE_STATIC: /* Next pointer */ s->list_pointer = g_list_next (s->list_pointer); if (s->list_pointer) { s->saved_item = s->list_pointer->data; } else { return FALSE; } item = s->saved_item; break; } } if (!item) { return FALSE; } if (!item->is_virtual && check_view (task->cfg->views, item->s->symbol, task)) { #ifdef HAVE_CLOCK_GETTIME # ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts1); # elif defined(HAVE_CLOCK_VIRTUAL) clock_gettime (CLOCK_VIRTUAL, &ts1); # else clock_gettime (CLOCK_REALTIME, &ts1); # endif #else if (gettimeofday (&tv1, NULL) == -1) { msg_warn ("gettimeofday failed: %s", strerror (errno)); } #endif if (G_UNLIKELY (check_debug_symbol (task->cfg, item->s->symbol))) { rspamd_log_debug (rspamd_main->logger); item->func (task, item->user_data); rspamd_log_nodebug (rspamd_main->logger); } else { item->func (task, item->user_data); } #ifdef HAVE_CLOCK_GETTIME # ifdef HAVE_CLOCK_PROCESS_CPUTIME_ID clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts2); # elif defined(HAVE_CLOCK_VIRTUAL) clock_gettime (CLOCK_VIRTUAL, &ts2); # else clock_gettime (CLOCK_REALTIME, &ts2); # endif #else if (gettimeofday (&tv2, NULL) == -1) { msg_warn ("gettimeofday failed: %s", strerror (errno)); } #endif #ifdef HAVE_CLOCK_GETTIME diff = (ts2.tv_sec - ts1.tv_sec) * 1000000 + (ts2.tv_nsec - ts1.tv_nsec) / 1000; #else diff = (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); #endif item->s->avg_time = set_counter (item->s->symbol, diff); } s->saved_item = item; return TRUE; }