3 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5 * Licensed under the Apache License, Version 2.0 (the License);
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
20 #include <sys/types.h>
30 /* Included for priorities level */
32 #include "loglimiter.h"
33 #include <logconfig.h>
35 /* Some random big odd number to make hash more diverse */
36 #define HASH_MAGIC_THINGY 5237231
41 /* TODO: List element handle, the list could be embedded
42 into structure some day, like kernel lists */
50 char tag[MAX_CONF_KEY_LEN];
53 typedef int (*hash_cmp_func_t)(struct rule*, struct rule*);
54 typedef int (*hash_match_func_t)(struct rule*, unsigned, const char*, int);
58 hash_match_func_t match;
63 struct hashmap* rules_hashmap = NULL;
65 /* Keep rules table as single-linked list */
66 struct rule* rules_table = NULL;
68 #define HASHMAP_MASK(hm) ((int)(hm->size - 1))
69 #define HASHMAP_LINEAR_PROBE_LEAP 1
71 static void rules_destroy(struct rule* rlist)
84 // LCOV_EXCL_START : disabled feature (limiter)
85 static int rule_compare(struct rule* r1, struct rule* r2)
87 if (r1->hash == r2->hash) {
88 if (r1->prio == r2->prio)
89 return strncmp(r1->tag, r2->tag, strlen(r2->tag));
91 return (r1->prio > r2->prio ? 1 : (-1));
94 return (r1->hash > r2->hash ? 1 : (-1));
97 static int rule_match(struct rule* r1, unsigned key, const char* s, int prio)
99 if (r1->hash == key) {
100 if (r1->prio == prio)
101 return strncmp(r1->tag, s, strlen(s));
103 return (r1->prio > prio ? 1 : (-1));
106 return (r1->hash > key ? 1 : (-1));
110 /* Translate fancy priority notation into common denominator */
111 static int util_prio_to_char(int prio)
113 static const char pri_table[DLOG_PRIO_MAX] = {
114 [DLOG_VERBOSE] = 'V',
123 if (DLOG_PRIO_MAX > prio && prio >= 0) {
124 return pri_table[prio];
127 case 'V': case 'v': case '1':
128 case 'D': case 'd': case '2':
129 case 'I': case 'i': case '3':
130 case 'W': case 'w': case '4':
131 case 'E': case 'e': case '5':
132 case 'F': case 'f': case '6':
133 case 'S': case 's': case '7':
145 /* The key is built from TAG and priority by DJB algorithm (Dan Bernstein).
146 * Key is only 31 bits long. Negative values are keep to hold NULL bucket */
147 static unsigned util_hash_key(const char* s, int c)
149 unsigned hash = (unsigned)c;
151 hash = ((hash << 5) + hash) + c;
156 while ('\0' != (c = *s++))
157 hash = ((hash << 5) + hash) + c;
160 /* Makes the hash more diverse */
161 hash *= HASH_MAGIC_THINGY;
166 // LCOV_EXCL_START : disabled feature (limiter)
167 /* Create hashmap, it's internal interface. */
168 static struct hashmap* hashmap_create(int size, hash_cmp_func_t cmp_func,
169 hash_match_func_t match_func)
171 struct hashmap* hm = NULL;
172 /* please keep hashmap fill ratio around 50% */
173 int internal_size = size << 1;
175 if (!cmp_func || !match_func || !size)
179 /* Round up the lines counter to next power of two. */
181 internal_size |= internal_size >> 1;
182 internal_size |= internal_size >> 2;
183 internal_size |= internal_size >> 4;
184 internal_size |= internal_size >> 8;
185 internal_size |= internal_size >> 16;
188 hm = malloc(sizeof(struct hashmap) + internal_size * sizeof(void*));
192 /* Initialize hash field to correct value */
193 memset((void*)hm, 0, sizeof(struct hashmap) + internal_size * sizeof(void*));
195 hm->size = internal_size;
197 hm->match = match_func;
202 static void hashmap_destroy(struct hashmap* hm)
210 static void hashmap_add(struct hashmap* hm, struct rule* r)
212 unsigned b = (r->hash & HASHMAP_MASK(hm));
214 while (hm->bucket[b]) {
215 if (!hm->cmp(r, (struct rule*)hm->bucket[b]))
217 b = (b + 1) & HASHMAP_MASK(hm);
223 static struct rule* hashmap_search(struct hashmap* hm, unsigned key,
224 const char* tag, int prio)
226 unsigned b = (key & HASHMAP_MASK(hm));
229 while (hm->bucket[b]) {
230 if (!hm->match(hm->bucket[b], key, tag, prio))
233 b = (b + 1) & HASHMAP_MASK(hm);
242 return hm->bucket[b];
245 /* Must be always executed after __log_config_read() */
246 int __log_limiter_initialize(void)
249 struct rule* rlist = NULL;
251 /* logconfig.c module had to initialize this correctly */
252 if (NULL == rules_table)
255 /* Count rules in the table */
263 /* Allocate hashmap */
264 rules_hashmap = (struct hashmap*) hashmap_create(hm_size,
267 if (NULL == rules_hashmap || !rules_hashmap->size)
270 /* Add rule to hashmap */
273 hashmap_add(rules_hashmap, rlist);
280 hashmap_destroy(rules_hashmap);
281 rules_destroy(rules_table);
283 rules_hashmap = NULL;
289 void __log_limiter_destroy(void)
291 hashmap_destroy(rules_hashmap);
292 rules_destroy(rules_table);
294 rules_hashmap = NULL;
297 int __log_limiter_add_rule(const char* tag, int prio, int limit)
304 r = (struct rule*) malloc(sizeof(struct rule));
308 memset(r, 0, sizeof(struct rule));
310 snprintf(r->tag, MAX_CONF_KEY_LEN, "%s", tag);
311 r->prio = util_prio_to_char(prio);
312 r->hash = util_hash_key(tag, r->prio);
314 r->start = time(NULL);
317 r->prev = rules_table;
323 // LCOV_EXCL_START : disabled feature (limiter)
325 /* Function implement logic needed to decide,
326 whenever message is written to log or not.
328 Possible return values are:
329 0 - to indicate that message is deny to write into log.
330 (-1) - to indicate that limit of the messages is reached.
331 1 - to indicate that message is allowed to write into log.
333 int __log_limiter_pass_log(const char* tag, int prio)
336 struct rule* r = NULL;
339 key = util_hash_key(tag, util_prio_to_char(prio));
340 r = hashmap_search(rules_hashmap, key, tag, util_prio_to_char(prio));
343 /* Rule not found, let's check general rule TAG:* */
344 key = util_hash_key(tag, '*');
345 r = hashmap_search(rules_hashmap, key, tag, '*');
347 /* Rule TAG:* not found,
348 let check general rule *:priority */
349 key = util_hash_key("*", util_prio_to_char(prio));
350 r = hashmap_search(rules_hashmap, key, "*",
351 util_prio_to_char(prio));
353 /* All known paths were exhausted,
354 use global rule *:* */
355 key = util_hash_key("*", '*');
356 r = hashmap_search(rules_hashmap, key, "*", '*');
358 /* *:* is not defined, so pass message through */
367 else if (__LOG_LIMITER_LIMIT_MAX < r->limit)
370 /* Decide, if it should go through or stop */
376 if (now - r->start <= TIME_FRAME) {
378 if (r->hit < r->limit) {
395 /* If everything failed, then pass message through */