3 * Copyright (c) 2013-2020 Samsung Electronics Co., Ltd
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is furnished
10 * to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 #include <sys/types.h>
36 /* Included for priorities level */
38 #include "loglimiter.h"
39 #include "loglimiter-internal.h"
40 #include <logconfig.h>
41 #include <ptrs_list.h>
42 #include <qos_defaults.h>
45 typedef int (*hash_cmp_func_t)(struct rule *, struct rule *);
46 typedef int (*hash_match_func_t)(struct rule *, unsigned, const char *, int);
50 hash_match_func_t match;
55 static list_head pid_rules = NULL;
57 static struct hashmap *rules_hashmap = NULL;
59 /* Keep rules table as single-linked list */
60 static struct rule *current_rules_table = NULL;
61 static struct rule *original_rules_table = NULL;
63 #define HASHMAP_MASK(hm) ((int)(hm->size - 1))
64 #define HASHMAP_LINEAR_PROBE_LEAP 1
66 static struct pid_limit *cached_pid_rule = NULL;
67 static struct pid_limit no_limit_rule = { .pid = -1, .limit = SIZE_MAX };
68 static size_t sent_by_me = 0;
69 static time_t last_pid_time = -1;
70 static time_t refresh_rate_s = -1;
71 static pid_t prev_pid = -1;
72 static bool already_exceeded = false;
74 void find_my_rule(elem_value value, void *userdata)
76 const pid_t my_pid = *(pid_t *)userdata;
77 struct pid_limit *const p = (struct pid_limit *) value;
83 static struct pass_log_result block_by_pid(void)
85 pid_t my_pid = getpid();
87 /* We cache the rule relevant to our pid since
88 * our pid doesn't usually change. Fork exists
89 * though so we have to pay attention lest we
90 * use an obsolete value. */
91 if (!cached_pid_rule || ((cached_pid_rule->pid != -1 || prev_pid != my_pid) && cached_pid_rule->pid != my_pid)) {
93 /* Forking resets the counter (mostly so that launcher
94 * log usage does not get inherited by innocent apps,
95 * since forking is otherwise fairly rare as far as
96 * typical dlog clients go). */
97 if (prev_pid != my_pid) {
99 already_exceeded = false;
103 cached_pid_rule = &no_limit_rule;
104 list_foreach(pid_rules, &my_pid, find_my_rule);
107 const time_t now = time(NULL);
108 if (now >= last_pid_time + refresh_rate_s) {
110 already_exceeded = false;
115 if (cached_pid_rule->limit > __LOG_LIMITER_LIMIT_MAX)
116 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
119 if (sent_by_me <= cached_pid_rule->limit)
120 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
121 else if (!already_exceeded) {
122 already_exceeded = true;
123 return (struct pass_log_result) {
124 .decision = DECISION_PID_LIMIT_EXCEEDED_MESSAGE,
125 .logs_per_period = cached_pid_rule->limit,
126 .period_s = refresh_rate_s,
129 return (struct pass_log_result) { .decision = DECISION_DENIED, };
132 static void rules_destroy(struct rule **rlist)
141 while ((r = *rlist)) {
142 *rlist = (*rlist)->prev;
147 static int rule_compare(struct rule *r1, struct rule *r2)
149 if (r1->hash == r2->hash) {
150 if (r1->prio == r2->prio)
151 return strncmp(r1->tag, r2->tag, strlen(r2->tag));
153 return (r1->prio > r2->prio ? 1 : (-1));
156 return (r1->hash > r2->hash ? 1 : (-1));
159 static int rule_match(struct rule *r1, unsigned key, const char *s, int prio)
161 if (r1->hash == key) {
162 if (r1->prio == prio)
163 return strncmp(r1->tag, s, strlen(s));
165 return (r1->prio > prio ? 1 : (-1));
168 return (r1->hash > key ? 1 : (-1));
171 /* Translate fancy priority notation into common denominator */
172 static int util_prio_to_char(int prio)
174 static const char pri_table[DLOG_PRIO_MAX] = {
175 [DLOG_UNKNOWN] = '?',
176 [DLOG_DEFAULT] = '*',
177 [DLOG_VERBOSE] = 'V',
204 return toupper(prio);
206 case '0' ... (DLOG_PRIO_MAX - 1 + '0'):
207 return pri_table[prio - '0'];
209 case 0 ... (DLOG_PRIO_MAX - 1):
210 return pri_table[prio];
217 uint32_t util_hash_key(const char *s, int c)
222 const uint32_t hash = create_hash(s, strlen(s));
224 // Hardly random, but it doesn't have to be.
228 /* Create hashmap, it's internal interface. */
229 static struct hashmap *hashmap_create(int size, hash_cmp_func_t cmp_func,
230 hash_match_func_t match_func)
236 struct hashmap *hm = NULL;
237 /* please keep hashmap fill ratio around 50% */
238 int internal_size = size << 1;
240 /* Round up the lines counter to next power of two. */
242 internal_size |= internal_size >> 1;
243 internal_size |= internal_size >> 2;
244 internal_size |= internal_size >> 4;
245 internal_size |= internal_size >> 8;
246 internal_size |= internal_size >> 16;
249 hm = malloc(sizeof(struct hashmap) + internal_size * sizeof(void *));
253 /* Initialize hash field to correct value */
254 memset((void *)hm, 0, sizeof(struct hashmap) + internal_size * sizeof(void *));
256 hm->size = internal_size;
258 hm->match = match_func;
263 static void hashmap_destroy(struct hashmap **hm)
274 static void hashmap_add(struct hashmap *hm, struct rule *r)
276 unsigned b = (r->hash & HASHMAP_MASK(hm));
278 while (hm->bucket[b]) {
279 if (!hm->cmp(r, (struct rule *)hm->bucket[b]))
281 b = (b + 1) & HASHMAP_MASK(hm);
287 static struct rule *hashmap_search(struct hashmap *hm,
288 const char *tag, char prio)
290 const unsigned key = util_hash_key(tag, prio);
292 unsigned b = (key & HASHMAP_MASK(hm));
295 while (hm->bucket[b]) {
296 if (!hm->match(hm->bucket[b], key, tag, prio))
299 b = (b + 1) & HASHMAP_MASK(hm);
307 return hm->bucket[b];
310 /* Must be always executed after __log_config_read() */
311 int __log_limiter_initialize(struct rule *rules_table)
314 struct rule *rlist = NULL;
316 /* logconfig.c module had to initialize this correctly */
317 if (NULL == rules_table)
320 /* Count rules in the table */
328 /* Allocate hashmap */
329 rules_hashmap = (struct hashmap *) hashmap_create(hm_size,
332 if (NULL == rules_hashmap || !rules_hashmap->size) {
333 hashmap_destroy(&rules_hashmap);
337 /* Add rule to hashmap */
340 hashmap_add(rules_hashmap, rlist);
347 static void destroy_hashmap_etc(void)
349 hashmap_destroy(&rules_hashmap);
350 rules_destroy(&original_rules_table);
353 void __log_limiter_destroy(void)
355 cached_pid_rule = NULL;
356 list_clear_free_contents(&pid_rules);
357 destroy_hashmap_etc();
360 struct rule *__log_limiter_add_rule(struct rule **rules_table, const char *tag, int prio, int limit)
366 for (struct rule *i = *rules_table; i; i = i->prev) {
367 if (strcmp(i->tag, tag) || i->prio != util_prio_to_char(prio))
370 if (i->limit != limit) { //set type here only if the limit is updated
372 i->type = RULE_DEFAULT;
377 r = (struct rule *) malloc(sizeof(*r));
381 memset(r, 0, sizeof(*r));
383 snprintf(r->tag, MAX_CONF_KEY_LEN, "%s", tag);
384 r->prio = util_prio_to_char(prio);
385 r->hash = util_hash_key(tag, r->prio);
387 r->start = time(NULL);
389 r->type = RULE_DEFAULT;
391 r->prev = *rules_table;
397 struct limiter_limits __log_limiter_get_limits(const char *tag, int prio)
399 const struct rule fallback = { .limit = -1 };
400 const char prio_c = util_prio_to_char(prio);
402 return (struct limiter_limits) {
403 .tag_and_prio = (hashmap_search(rules_hashmap, tag, prio_c) ?: &fallback)->limit,
404 .tag = (hashmap_search(rules_hashmap, tag, '*') ?: &fallback)->limit,
405 .prio = (hashmap_search(rules_hashmap, "*", prio_c) ?: &fallback)->limit,
406 .global = (hashmap_search(rules_hashmap, "*", '*') ?: &fallback)->limit,
411 /* Function implement logic needed to decide,
412 * whenever message is written to log or not.
414 * On first time being blocked, it will return *_LIMIT_EXCEEDED_MESSAGE in the decision field,
415 * which allows the callee to write a relevant message to logs.
417 struct pass_log_result __log_limiter_pass_log(const char *tag, int prio)
419 struct pass_log_result pid_result = block_by_pid();
420 if (pid_result.decision != DECISION_ALLOWED)
424 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
426 /* allow empty-tagged messages and make it easy to catch an application that does that */
428 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
430 const char prio_c = util_prio_to_char(prio);
432 hashmap_search(rules_hashmap, tag, prio_c) ?:
433 hashmap_search(rules_hashmap, tag, '*') ?:
434 hashmap_search(rules_hashmap, "*", prio_c) ?:
435 hashmap_search(rules_hashmap, "*", '*');
438 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
440 return (struct pass_log_result) { .decision = DECISION_DENIED, };
441 if (__LOG_LIMITER_LIMIT_MAX < r->limit)
442 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
444 /* Decide, if it should go through or stop */
445 time_t now = time(NULL);
448 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
450 if (now - r->start <= refresh_rate_s) {
452 if (r->hit < r->limit) {
454 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
457 return (struct pass_log_result) {
458 .decision = DECISION_TAG_LIMIT_EXCEEDED_MESSAGE,
459 .logs_per_period = r->limit,
460 .period_s = refresh_rate_s,
464 return (struct pass_log_result) { .decision = DECISION_DENIED, };
469 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
474 * @brief Limiter rule from config
475 * @details Adds a limiter rule from a config entry, if one exists inside
476 * @param[in] key The entry key
477 * @param[in] value The entry value
478 * @param[in,out] userdata A pointer to the rules table (pointer to struct rule *)
480 static void regular_limiter_iteration(const char *key, const char *value, void *userdata)
484 struct rule **rules_table = (struct rule **)userdata;
487 static const int prefix_len = sizeof("limiter|") - 1;
488 char * delimiter_pos;
489 char limiter_tag[MAX_CONF_KEY_LEN];
492 if (strncmp(key, "limiter|", prefix_len))
495 delimiter_pos = strchr(key + prefix_len + 1, '|');
496 if (!delimiter_pos || (delimiter_pos + 1 == key + strlen(key)))
499 snprintf(limiter_tag, delimiter_pos - (key + prefix_len) + 1, "%s", key + prefix_len);
501 if (!strcmp(value, "allow"))
502 limit = __LOG_LIMITER_LIMIT_MAX + 1;
503 else if (!strcmp(value, "deny"))
508 __log_limiter_add_rule(rules_table, limiter_tag, *(delimiter_pos + 1), limit);
511 static void pid_limiter_iteration(const char *key, const char *value, void *userdata)
513 if (strncmp(key, "pidlimit|", sizeof "pidlimit|" - 1))
516 int pid = atoi(key + sizeof "pidlimit|" - 1);
519 if (strcmp(value, "deny") == 0)
521 else if (strcmp(value, "allow") == 0)
522 limit = __LOG_LIMITER_LIMIT_MAX + 1;
526 struct pid_limit *const p = malloc(sizeof *p);
532 list_add(&pid_rules, p);
535 static void __config_iteration(const char *key, const char *value, void *userdata)
537 regular_limiter_iteration(key, value, userdata);
538 pid_limiter_iteration(key, value, userdata);
541 int __log_limiter_create(const struct log_config *config)
544 assert(!original_rules_table);
545 assert(!rules_hashmap);
547 last_pid_time = time(NULL);
548 cached_pid_rule = NULL;
549 log_config_foreach(config, __config_iteration, &original_rules_table);
550 refresh_rate_s = log_config_get_int(config, "qos_refresh_rate_s", DEFAULT_QOS_LIMIT_DURATION_S);
552 const int r = __log_limiter_initialize(original_rules_table);
554 rules_destroy(&original_rules_table);
555 return pid_rules != NULL;
558 for (struct rule *i = original_rules_table; i; i = i->prev)
559 i->type = RULE_STATIC;
564 void __log_limiter_update(const struct log_config *config)
567 assert(!original_rules_table || rules_hashmap);
569 struct rule *rules_table = NULL;
570 for (struct rule *i = original_rules_table; i; i = i->prev) {
571 struct rule *r = __log_limiter_add_rule(&rules_table, i->tag, i->prio, i->limit);
574 r->type = RULE_STATIC;
577 cached_pid_rule = NULL;
578 list_clear_free_contents(&pid_rules);
579 log_config_foreach(config, __config_iteration, &rules_table);
582 for (struct rule *i = rules_table; i; i = i->prev) {
583 const char prio_c = util_prio_to_char(i->prio);
584 const struct rule *const prev_rule = hashmap_search(rules_hashmap, i->tag, prio_c);
588 i->hit = prev_rule->hit;
589 i->start = prev_rule->start;
593 hashmap_destroy(&rules_hashmap);
594 if (__log_limiter_initialize(rules_table) < 0) {
595 destroy_hashmap_etc();
599 rules_destroy(¤t_rules_table);
600 current_rules_table = rules_table;
604 * @brief Dump a limiter rule to provided buffer
605 * @details Formats a string containing information about a rule and inserts it into provided buffer
606 * @param[in,out] r The pointer to the rule to be dumped (in), the pointer to the previous rule (out)
607 * @param[in,out] buf The buffer to dump the rule into
608 * @param[in] size The size of the buffer
609 * @returns 0 on success, -1 on failure
611 int __log_limiter_dump_rule(struct rule **r, char *buf, const size_t size)
613 struct rule *ruleptr = *r ? : current_rules_table;
621 if (ruleptr->limit == 0)
622 s = snprintf(buf, size, "Denied ");
623 else if (ruleptr->limit == (__LOG_LIMITER_LIMIT_MAX + 1))
624 s = snprintf(buf, size, "Unlimited ");
626 s = snprintf(buf, size, "%d logs/minute ", ruleptr->limit);
631 s = snprintf(buf + s, size - s, "for %s:%c (%s)",
633 util_prio_to_char(ruleptr->prio),
634 (ruleptr->type == RULE_STATIC) ? "static" : "dynamic");
642 list_head __log_limiter_get_pid_limits(void)