diff options
Diffstat (limited to 'src/libdlog')
-rwxr-xr-x | src/libdlog/log.c | 305 | ||||
-rw-r--r-- | src/libdlog/logconfig.c | 174 | ||||
-rw-r--r-- | src/libdlog/loglimiter.c | 419 |
3 files changed, 898 insertions, 0 deletions
diff --git a/src/libdlog/log.c b/src/libdlog/log.c new file mode 100755 index 0000000..46fa2d1 --- /dev/null +++ b/src/libdlog/log.c @@ -0,0 +1,305 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*- + * DLOG + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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 <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <sys/syscall.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <dlog.h> +#include "loglimiter.h" +#include "logconfig.h" +#ifdef HAVE_SYSTEMD_JOURNAL +#define SD_JOURNAL_SUPPRESS_LOCATION 1 +#include <syslog.h> +#include <systemd/sd-journal.h> +#endif +#ifdef FATAL_ON +#include <assert.h> +#endif + +#define LOG_BUF_SIZE 1024 + +#define LOG_MAIN "log_main" +#define LOG_RADIO "log_radio" +#define LOG_SYSTEM "log_system" +#define LOG_APPS "log_apps" + +#define VALUE_MAX 2 +#define LOG_CONFIG_FILE "/opt/etc/dlog.conf" + +#ifndef HAVE_SYSTEMD_JOURNAL +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; +#endif +static int (*write_to_log)(log_id_t, log_priority, const char *tag, const char *msg) = NULL; +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +static struct log_config config; + +#ifdef HAVE_SYSTEMD_JOURNAL +static inline int dlog_pri_to_journal_pri(log_priority prio) +{ + static int pri_table[DLOG_PRIO_MAX] = { + [DLOG_UNKNOWN] = LOG_DEBUG, + [DLOG_DEFAULT] = LOG_DEBUG, + [DLOG_VERBOSE] = LOG_DEBUG, + [DLOG_DEBUG] = LOG_DEBUG, + [DLOG_INFO] = LOG_INFO, + [DLOG_WARN] = LOG_WARNING, + [DLOG_ERROR] = LOG_ERR, + [DLOG_FATAL] = LOG_CRIT, + [DLOG_SILENT] = -1, + }; + + if (prio < 0 || prio >= DLOG_PRIO_MAX) + return DLOG_ERROR_INVALID_PARAMETER; + + return pri_table[prio]; +} + +static inline const char* dlog_id_to_string(log_id_t log_id) +{ + static const char* id_table[LOG_ID_MAX] = { + [LOG_ID_MAIN] = LOG_MAIN, + [LOG_ID_RADIO] = LOG_RADIO, + [LOG_ID_SYSTEM] = LOG_SYSTEM, + [LOG_ID_APPS] = LOG_APPS, + }; + + if (log_id < 0 || log_id >= LOG_ID_MAX || !id_table[log_id]) + return "UNKNOWN"; + + return id_table[log_id]; +} + +static int __write_to_log_sd_journal(log_id_t log_id, log_priority prio, const char *tag, const char *msg) +{ + pid_t tid = (pid_t)syscall(SYS_gettid); + /* XXX: sd_journal_sendv() with manually filed iov-s might be faster */ + return sd_journal_send("MESSAGE=%s", msg, + "PRIORITY=%i", dlog_pri_to_journal_pri(prio), + "LOG_TAG=%s", tag, + "LOG_ID=%s", dlog_id_to_string(log_id), + "TID=%d", tid, + NULL); +} + +#else +static int __write_to_log_null(log_id_t log_id, log_priority prio, const char *tag, const char *msg) +{ + return DLOG_ERROR_NOT_PERMITTED; +} + +static int __write_to_log_kernel(log_id_t log_id, log_priority prio, const char *tag, const char *msg) +{ + ssize_t ret; + int log_fd; + struct iovec vec[3]; + + if (log_id < LOG_ID_MAX) + log_fd = log_fds[log_id]; + else + return DLOG_ERROR_INVALID_PARAMETER; + + if (prio < DLOG_VERBOSE || prio >= DLOG_PRIO_MAX) + return DLOG_ERROR_INVALID_PARAMETER; + + if (!tag) + tag = ""; + + if (!msg) + return DLOG_ERROR_INVALID_PARAMETER; + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + ret = writev(log_fd, vec, 3); + if (ret < 0) + ret = errno; + + return ret; +} +#endif + +static void __configure(void) +{ + if (0 > __log_config_read(LOG_CONFIG_FILE, &config)) { + config.lc_limiter = 0; + config.lc_plog = 0; + } + + if (config.lc_limiter) { + if (0 > __log_limiter_initialize()) { + config.lc_limiter = 0; + } + } +} + +static void __dlog_init(void) +{ + pthread_mutex_lock(&log_init_lock); + /* configuration */ + __configure(); +#ifdef HAVE_SYSTEMD_JOURNAL + write_to_log = __write_to_log_sd_journal; +#else + /* open device */ + log_fds[LOG_ID_MAIN] = open("/dev/"LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = open("/dev/"LOG_SYSTEM, O_WRONLY); + log_fds[LOG_ID_RADIO] = open("/dev/"LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_APPS] = open("/dev/"LOG_APPS, O_WRONLY); + if (log_fds[LOG_ID_MAIN] < 0) { + write_to_log = __write_to_log_null; + } else { + write_to_log = __write_to_log_kernel; + } + if (log_fds[LOG_ID_RADIO] < 0) + log_fds[LOG_ID_RADIO] = log_fds[LOG_ID_MAIN]; + if (log_fds[LOG_ID_SYSTEM] < 0) + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + if (log_fds[LOG_ID_APPS] < 0) + log_fds[LOG_ID_APPS] = log_fds[LOG_ID_MAIN]; +#endif + pthread_mutex_unlock(&log_init_lock); +} + +void __dlog_fatal_assert(int prio) +{ +#ifdef FATAL_ON + assert(!(prio == DLOG_FATAL)); +#endif +} + +static int dlog_should_log(log_id_t log_id, const char* tag, int prio) +{ + int should_log; + +#ifndef TIZEN_DEBUG_ENABLE + if (prio <= DLOG_DEBUG) + return DLOG_ERROR_INVALID_PARAMETER; +#endif + if (!tag) + return DLOG_ERROR_INVALID_PARAMETER; + + if (log_id < 0 || LOG_ID_MAX <= log_id) + return DLOG_ERROR_INVALID_PARAMETER; + + if (log_id != LOG_ID_APPS && !config.lc_plog) + return DLOG_ERROR_NOT_PERMITTED; + + if (config.lc_limiter) { + should_log = __log_limiter_pass_log(tag, prio); + + if (!should_log) { + return DLOG_ERROR_NOT_PERMITTED; + } else if (should_log < 0) { + write_to_log(log_id, prio, tag, + "Your log has been blocked due to limit of log lines per minute."); + return DLOG_ERROR_NOT_PERMITTED; + } + } + + return DLOG_ERROR_NONE; +} + +int __dlog_vprint(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap) +{ + int ret; + char buf[LOG_BUF_SIZE]; + + if (write_to_log == NULL) + __dlog_init(); + + ret = dlog_should_log(log_id, tag, prio); + + if (ret < 0) + return ret; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + ret = write_to_log(log_id, prio, tag, buf); +#ifdef FATAL_ON + __dlog_fatal_assert(prio); +#endif + return ret; +} + +int __dlog_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...) +{ + int ret; + va_list ap; + char buf[LOG_BUF_SIZE]; + + if (write_to_log == NULL) + __dlog_init(); + + ret = dlog_should_log(log_id, tag, prio); + + if (ret < 0) + return ret; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + ret = write_to_log(log_id, prio, tag, buf); +#ifdef FATAL_ON + __dlog_fatal_assert(prio); +#endif + return ret; +} + +int dlog_vprint(log_priority prio, const char *tag, const char *fmt, va_list ap) +{ + char buf[LOG_BUF_SIZE]; + + if (write_to_log == NULL) + __dlog_init(); + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + + return write_to_log(LOG_ID_APPS, prio, tag, buf); +} + +int dlog_print(log_priority prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + if (write_to_log == NULL) + __dlog_init(); + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return write_to_log(LOG_ID_APPS, prio, tag, buf); +} + +void __attribute__((destructor)) __dlog_fini(void) +{ + __log_limiter_destroy(); +} diff --git a/src/libdlog/logconfig.c b/src/libdlog/logconfig.c new file mode 100644 index 0000000..6c7eabc --- /dev/null +++ b/src/libdlog/logconfig.c @@ -0,0 +1,174 @@ +/* + * DLOG + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "logconfig.h" +#include "loglimiter.h" + +/* Functions possible return value */ +#define RET_ERROR (-1) +#define RET_SUCCESS 0 + +#define CONFIG_LINE_MAX_LEN 256 +#define CONFIG_OPTION_MAX_LEN 64 + +/* Shortcut macros */ +#define isCOMMENT(c) ('#' == c) +#define isNEWLINE(c) ('\n' == c || '\r' == c) + +/* Dlog options definition */ +#define LOG_PLATFORM_STRING "PlatformLogs" +#define LOG_LIMITER_STRING "LogLimiter" + +/* Define options possible values */ +#define ALLOW_STRING "allow" +#define DENY_STRING "deny" +#define ON_STRING "on" +#define OFF_STRING "off" +#define isYES(c) (c == '1') +#define isNO(c) (c == '0') + + +static int log_config_multiplex_opt(char* opt_str, char* val_str, int prio, + struct log_config* config) +{ + int value = 0; + + /* There are only two ways to interpret the lines, so make here a short circuit */ + if (0 < prio) { /* Is it a rule or an option ? */ + /* For the filtering rule ... */ + + if (!strncasecmp(ALLOW_STRING, val_str, sizeof(ALLOW_STRING))) { + value = __LOG_LIMITER_LIMIT_MAX + 1; + } else if (!strncasecmp(DENY_STRING, val_str, + sizeof(DENY_STRING))) { + value = 0; + } else { + char* endptr = NULL; + + value = strtoul(val_str, &endptr, 0); + if (*endptr != '\0') { + return RET_ERROR; + } + } + + return __log_limiter_add_rule(opt_str, prio, value); + + } else { /* It's an option then */ + if (isYES(*val_str)) { + value = 1; + } else if (isNO(*val_str)) { + value = 0; + } else if (!strncasecmp(ON_STRING, val_str, + sizeof(ON_STRING))) { + value = 1; + } else if (!strncasecmp(OFF_STRING, val_str, + sizeof(OFF_STRING))) { + value = 0; + } else { + return RET_ERROR; + } + + if (!strncasecmp(LOG_PLATFORM_STRING, opt_str, + sizeof(LOG_PLATFORM_STRING))) { + config->lc_plog = value; + } else if (!strncasecmp(LOG_LIMITER_STRING, opt_str, + sizeof(LOG_LIMITER_STRING))) { + config->lc_limiter = value; + } else { + return RET_ERROR; + } + } + + return RET_SUCCESS; +} + +/* Function returns 0 for success or -1 when error occurred */ +int __log_config_read(const char* config_file, struct log_config* config) +{ + FILE* fconfig = NULL; + char buf[CONFIG_LINE_MAX_LEN]; + char opt[CONFIG_OPTION_MAX_LEN]; + char opt_value[CONFIG_OPTION_MAX_LEN]; + int prio = (-1); + int ret = 0; + + /* Check input */ + if (NULL == config_file || NULL == config) { + return RET_ERROR; + } + + if (NULL == (fconfig = fopen(config_file, "r"))) { + return RET_ERROR; + } + + while (1) { + memset(buf, 0, CONFIG_LINE_MAX_LEN); + errno = 0; + if (NULL == fgets(buf, CONFIG_LINE_MAX_LEN, fconfig)) { + if (!errno) { + break; + } + goto bailout; + } + + /* We ignore comments and blank lines */ + if (isCOMMENT(*buf) || isNEWLINE(*buf)) { + continue; + } + + memset(opt, 0, sizeof(opt)); + memset(opt_value, 0, sizeof(opt_value)); + prio = (-1); + /* Read configure line, sscanf() should return two tokens, + * even for tag filtering rule */ + ret = sscanf(buf, "%[A-z0-9-]\t%[A-z0-9]", + opt, opt_value); + if (ret != 2) { /* The line is malformed ? */ + char c = 0; + /* This could be rule with space inside TAG */ + ret = sscanf(buf, "\"%[]A-z0-9*\x20_+:;/-]\"\t|\t%c\t%[A-z0-9]", + opt, &c, opt_value); + if (ret != 3) { + goto bailout; + } + prio = (int)c; + } + + + if (0 > log_config_multiplex_opt(opt, opt_value, prio, config)) { + goto bailout; + } + } + + fclose(fconfig); + return RET_SUCCESS; + +bailout: + /* These actions should warranty that + we cleanly handle initialization errors */ + fclose(fconfig); + /* Clean rules list to prevent log limiter initialization, + if configuration error occured. */ + __log_limiter_rules_purge(); + + return RET_ERROR; +} diff --git a/src/libdlog/loglimiter.c b/src/libdlog/loglimiter.c new file mode 100644 index 0000000..a0e874e --- /dev/null +++ b/src/libdlog/loglimiter.c @@ -0,0 +1,419 @@ +/* + * DLOG + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * 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 <stddef.h> +#include <limits.h> +#include <sys/types.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <pthread.h> +#include <sys/time.h> +#include <time.h> + +/* Included for priorities level */ +#include <dlog.h> +#include "loglimiter.h" + +/* Defines maximal meaningful tag length */ +#define TAG_REASONABLE_LEN 32 + +/* Some random big odd number to make hash more diverse */ +#define HASH_MAGIC_THINGY 5237231 + +#define TIME_FRAME 60 + +struct rule{ + /* TODO: List element handle, the list could be embedded + into structure some day, like kernel lists */ + struct rule* prev; + + unsigned hash; + int prio; + int limit; + int hit; + time_t start; + char tag[TAG_REASONABLE_LEN]; +}; + +typedef int (*hash_cmp_func_t)(struct rule*, struct rule*); +typedef int (*hash_match_func_t)(struct rule*, unsigned, const char*, int); + +struct hashmap { + hash_cmp_func_t cmp; + hash_match_func_t match; + int size; + void* bucket[]; +}; + +struct hashmap* rules_hashmap = NULL; + +/* Keep rules table as single-linked list */ +struct rule* rules_table = NULL; + +#define HASHMAP_MASK(hm) ((int)(hm->size - 1)) +#define HASHMAP_LINEAR_PROBE_LEAP 1 + +static void rules_destroy(struct rule* rlist) +{ + struct rule* r; + + if (NULL == rlist) { + return; + } + + while ((r = rlist)) { + rlist = rlist->prev; + free(r); + } +} + +void __log_limiter_rules_purge(void) +{ + rules_destroy(rules_table); + rules_table = NULL; +} + +static int rule_compare(struct rule* r1, struct rule* r2) +{ + if (r1->hash == r2->hash) { + if (r1->prio == r2->prio) { + return strncmp(r1->tag, r2->tag, strlen(r2->tag)); + } else { + return (r1->prio > r2->prio ? 1 : (-1)); + } + } + + return (r1->hash > r2->hash ? 1 : (-1)); +} + +static int rule_match(struct rule* r1, unsigned key, const char* s, int prio) +{ + if (r1->hash == key) { + if (r1->prio == prio) { + return strncmp(r1->tag, s, strlen(s)); + } else { + return (r1->prio > prio ? 1 : (-1)); + } + } + + return (r1->hash > key ? 1 : (-1)); +} + +/* Translate fancy priority notation into common denominator */ +static int util_prio_to_char(int prio) +{ + static const char pri_table[DLOG_PRIO_MAX] = { + [DLOG_VERBOSE] = 'V', + [DLOG_DEBUG] = 'D', + [DLOG_INFO] = 'I', + [DLOG_WARN] = 'W', + [DLOG_ERROR] = 'E', + [DLOG_FATAL] = 'F', + [DLOG_SILENT] = 'S', + }; + + if (DLOG_PRIO_MAX > prio && prio >= 0) { + return pri_table[prio]; + } else { + switch (prio) { + case 'V': case 'v': case '1': + case 'D': case 'd': case '2': + case 'I': case 'i': case '3': + case 'W': case 'w': case '4': + case 'E': case 'e': case '5': + case 'F': case 'f': case '6': + case 'S': case 's': case '7': + case '*': + return prio; + + default: + ;; + } + } + + return '?'; +} + +/* The key is built from TAG and priority by DJB algorithm (Dan Bernstein). + * Key is only 31 bits long. Negative values are keep to hold NULL bucket */ +static unsigned util_hash_key(const char* s, int c) +{ + unsigned hash = (unsigned)c; + + hash = ((hash << 5) + hash) + c; + + if (!s || !s[0]) { + goto finish; + } + + while ('\0' != (c = *s++)) { + hash = ((hash << 5) + hash) + c; + } + +finish: + /* Makes the hash more diverse */ + hash *= HASH_MAGIC_THINGY; + + return hash; +} + +/* Create hashmap, it's internal interface. */ +static struct hashmap* hashmap_create(int size, hash_cmp_func_t cmp_func, + hash_match_func_t match_func) +{ + struct hashmap* hm = NULL; + /* please keep hashmap fill ratio around 50% */ + int internal_size = size << 1; + + if (!cmp_func || !match_func || !size) { + return NULL; + } + + + /* Round up the lines counter to next power of two. */ + internal_size--; + internal_size |= internal_size >> 1; + internal_size |= internal_size >> 2; + internal_size |= internal_size >> 4; + internal_size |= internal_size >> 8; + internal_size |= internal_size >> 16; + internal_size++; + + hm = malloc(sizeof(struct hashmap) + internal_size * sizeof(void*)); + if (!hm) { + return NULL; + } + /* Initialize hash field to correct value */ + memset((void*)hm, 0, sizeof(struct hashmap) + internal_size * sizeof(void*)); + + hm->size = internal_size; + hm->cmp = cmp_func; + hm->match = match_func; + + return hm; +} + +static void hashmap_destroy(struct hashmap* hm) +{ + if (hm) { + hm->size = 0; + free(hm); + } +} + +static void hashmap_add(struct hashmap* hm, struct rule* r) +{ + unsigned b = (r->hash & HASHMAP_MASK(hm)); + + while (hm->bucket[b]) { + if (!hm->cmp(r, (struct rule*)hm->bucket[b])) { + break; + } + b = (b + 1) & HASHMAP_MASK(hm); + } + + hm->bucket[b] = r; +} + +static struct rule* hashmap_search(struct hashmap* hm, unsigned key, + const char* tag, int prio) +{ + unsigned b = (key & HASHMAP_MASK(hm)); + unsigned b0 = b; + + while (hm->bucket[b]) { + if (!hm->match(hm->bucket[b], key, tag, prio)) { + break; + } + + b = (b + 1) & HASHMAP_MASK(hm); + + if (b0 == b) { + return NULL; + } + } + + if (!hm->bucket[b]) { + return NULL; + } + + return hm->bucket[b]; +} + +/* Must be always executed after __log_config_read() */ +int __log_limiter_initialize(void) +{ + int hm_size; + struct rule* rlist = NULL; + + /* logconfig.c module had to initialize this correctly */ + if (NULL == rules_table) { + return (-1); + } + + /* Count rules in the table */ + hm_size = 0; + rlist = rules_table; + while (rlist) { + hm_size++; + rlist = rlist->prev; + } + + /* Allocate hashmap */ + rules_hashmap = (struct hashmap*) hashmap_create(hm_size, + &rule_compare, + &rule_match); + if (NULL == rules_hashmap || !rules_hashmap->size) { + goto bailout; + } + + /* Add rule to hashmap */ + rlist = rules_table; + while (rlist) { + hashmap_add(rules_hashmap, rlist); + rlist = rlist->prev; + } + + return 0; + +bailout: + hashmap_destroy(rules_hashmap); + rules_destroy(rules_table); + rules_table = NULL; + rules_hashmap = NULL; + + return (-1); +} + +void __log_limiter_destroy(void) +{ + hashmap_destroy(rules_hashmap); + rules_destroy(rules_table); + rules_table = NULL; + rules_hashmap = NULL; +} + +int __log_limiter_add_rule(const char* tag, int prio, int limit) +{ + struct rule* r; + + if (!tag) { + return (-1); + } + + r = (struct rule*) malloc(sizeof(struct rule)); + if (NULL == r) { + return (-1); + } + memset(r, 0, sizeof(struct rule)); + + snprintf(r->tag, TAG_REASONABLE_LEN, "%s", tag); + r->prio = util_prio_to_char(prio); + r->hash = util_hash_key(tag, r->prio); + r->limit = limit; + r->start = time(NULL); + r->hit = 0; + + r->prev = rules_table; + rules_table = r; + + return 0; +} + + +/* Function implement logic needed to decide, + whenever message is written to log or not. + + Possible return values are: + 0 - to indicate that message is deny to write into log. + (-1) - to indicate that limit of the messages is reached. + 1 - to indicate that message is allowed to write into log. +*/ +int __log_limiter_pass_log(const char* tag, int prio) +{ + unsigned key = 0; + struct rule* r = NULL; + time_t now = 0; + + key = util_hash_key(tag, util_prio_to_char(prio)); + r = hashmap_search(rules_hashmap, key, tag, util_prio_to_char(prio)); + + if (!r) { + /* Rule not found, let's check general rule TAG:* */ + key = util_hash_key(tag, '*'); + r = hashmap_search(rules_hashmap, key, tag, '*'); + if (!r) { + /* Rule TAG:* not found, + let check general rule *:priority */ + key = util_hash_key("*", util_prio_to_char(prio)); + r = hashmap_search(rules_hashmap, key, "*", + util_prio_to_char(prio)); + if (!r) { + /* All known paths were exhausted, + use global rule *:* */ + key = util_hash_key("*", '*'); + r = hashmap_search(rules_hashmap, key, "*", '*'); + + /* *:* is not defined, so pass message through */ + if (!r) { + return 1; + } + } + } + } + + if (!r->limit) { + return 0; + } else if (__LOG_LIMITER_LIMIT_MAX < r->limit) { + return 1; + } + + /* Decide, if it should go through or stop */ + now = time(NULL); + + if (0 > now) { + return 1; + } + + if (now - r->start <= TIME_FRAME) { + if (r->hit >= 0) { + if (r->hit < r->limit) { + r->hit++; + return 1; + } + r->hit = INT_MIN+1; + return (-1); + } else { + r->hit++; + return 0; + } + + } else { + r->start = now; + r->hit = 0; + return 1; + } + + /* If everything failed, then pass message through */ + return 1; +} |