summaryrefslogtreecommitdiff
path: root/src/libdlog
diff options
context:
space:
mode:
Diffstat (limited to 'src/libdlog')
-rwxr-xr-xsrc/libdlog/log.c305
-rw-r--r--src/libdlog/logconfig.c174
-rw-r--r--src/libdlog/loglimiter.c419
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;
+}