Replace () with (void) in function prototypes
[platform/core/system/dlog.git] / src / shared / loglimiter.c
1 /*  MIT License
2  *
3  * Copyright (c) 2013-2020 Samsung Electronics Co., Ltd
4  *
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:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
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
21  * THE SOFTWARE. */
22
23 #include <assert.h>
24 #include <stddef.h>
25 #include <limits.h>
26 #include <sys/types.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <pthread.h>
34 #include <sys/time.h>
35
36 /* Included for priorities level */
37 #include <dlog.h>
38 #include "loglimiter.h"
39 #include "loglimiter-internal.h"
40 #include <logconfig.h>
41 #include <ptrs_list.h>
42 #include <qos_defaults.h>
43 #include <hash.h>
44
45 typedef int (*hash_cmp_func_t)(struct rule *, struct rule *);
46 typedef int (*hash_match_func_t)(struct rule *, unsigned, const char *, int);
47
48 struct hashmap {
49         hash_cmp_func_t cmp;
50         hash_match_func_t match;
51         int size;
52         void *bucket[];
53 };
54
55 static list_head pid_rules = NULL;
56
57 static struct hashmap *rules_hashmap = NULL;
58
59 /* Keep rules table as single-linked list */
60 static struct rule *current_rules_table = NULL;
61 static struct rule *original_rules_table = NULL;
62
63 #define HASHMAP_MASK(hm)          ((int)(hm->size - 1))
64 #define HASHMAP_LINEAR_PROBE_LEAP 1
65
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;
73
74 void find_my_rule(elem_value value, void *userdata)
75 {
76         const pid_t my_pid = *(pid_t *)userdata;
77         struct pid_limit *const p = (struct pid_limit *) value;
78
79         if (p->pid == my_pid)
80                 cached_pid_rule = p;
81 }
82
83 static struct pass_log_result block_by_pid(void)
84 {
85         pid_t my_pid = getpid();
86
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)) {
92
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) {
98                         last_pid_time = -1;
99                         already_exceeded = false;
100                         sent_by_me = 0;
101                 }
102
103                 cached_pid_rule = &no_limit_rule;
104                 list_foreach(pid_rules, &my_pid, find_my_rule);
105         }
106
107         const time_t now = time(NULL);
108         if (now >= last_pid_time + refresh_rate_s) {
109                 last_pid_time = now;
110                 already_exceeded = false;
111                 sent_by_me = 1;
112         } else
113                 sent_by_me += 1;
114
115         if (cached_pid_rule->limit > __LOG_LIMITER_LIMIT_MAX)
116                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
117
118         prev_pid = my_pid;
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,
127                 };
128         } else
129                 return (struct pass_log_result) { .decision = DECISION_DENIED, };
130 }
131
132 static void rules_destroy(struct rule **rlist)
133 {
134         assert(rlist);
135
136         struct rule *r;
137
138         if (NULL == *rlist)
139                 return;
140
141         while ((r = *rlist)) {
142                 *rlist = (*rlist)->prev;
143                 free(r);
144         }
145 }
146
147 static int rule_compare(struct rule *r1, struct rule *r2)
148 {
149         if (r1->hash == r2->hash) {
150                 if (r1->prio == r2->prio)
151                         return strncmp(r1->tag, r2->tag, strlen(r2->tag));
152                 else
153                         return (r1->prio > r2->prio ? 1 : (-1));
154         }
155
156         return (r1->hash > r2->hash ? 1 : (-1));
157 }
158
159 static int rule_match(struct rule *r1, unsigned key, const char *s, int prio)
160 {
161         if (r1->hash == key) {
162                 if (r1->prio == prio)
163                         return strncmp(r1->tag, s, strlen(s));
164                 else
165                         return (r1->prio > prio ? 1 : (-1));
166         }
167
168         return (r1->hash > key ? 1 : (-1));
169 }
170
171 /* Translate fancy priority notation into common denominator */
172 static int util_prio_to_char(int prio)
173 {
174         static const char pri_table[DLOG_PRIO_MAX] = {
175                 [DLOG_UNKNOWN] = '?',
176                 [DLOG_DEFAULT] = '*',
177                 [DLOG_VERBOSE] = 'V',
178                 [DLOG_DEBUG] = 'D',
179                 [DLOG_INFO] = 'I',
180                 [DLOG_WARN] = 'W',
181                 [DLOG_ERROR] = 'E',
182                 [DLOG_FATAL] = 'F',
183                 [DLOG_SILENT] = 'S',
184         };
185
186         switch (prio) {
187         case 'V':
188         case 'D':
189         case 'I':
190         case 'W':
191         case 'E':
192         case 'F':
193         case 'S':
194         case '*':
195                 return prio;
196
197         case 'v':
198         case 'd':
199         case 'i':
200         case 'w':
201         case 'e':
202         case 'f':
203         case 's':
204                 return toupper(prio);
205
206         case '0' ... (DLOG_PRIO_MAX - 1 + '0'):
207                 return pri_table[prio - '0'];
208
209         case 0 ... (DLOG_PRIO_MAX - 1):
210                 return pri_table[prio];
211
212         default:
213                 return '?';
214         }
215 }
216
217 uint32_t util_hash_key(const char *s, int c)
218 {
219         if (!s)
220                 return 0;
221
222         const uint32_t hash = create_hash(s, strlen(s));
223
224         // Hardly random, but it doesn't have to be.
225         return hash ^ c;
226 }
227
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)
231 {
232         assert(cmp_func);
233         assert(match_func);
234         assert(size > 0);
235
236         struct hashmap *hm = NULL;
237         /* please keep hashmap fill ratio around 50% */
238         int internal_size = size << 1;
239
240         /* Round up the lines counter to next power of two. */
241         internal_size--;
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;
247         internal_size++;
248
249         hm = malloc(sizeof(struct hashmap) + internal_size * sizeof(void *));
250         if (!hm)
251                 return NULL;
252
253         /* Initialize hash field to correct value */
254         memset((void *)hm, 0, sizeof(struct hashmap) + internal_size * sizeof(void *));
255
256         hm->size = internal_size;
257         hm->cmp = cmp_func;
258         hm->match = match_func;
259
260         return hm;
261 }
262
263 static void hashmap_destroy(struct hashmap **hm)
264 {
265         assert(hm);
266
267         if (!*hm)
268                 return;
269
270         free(*hm);
271         *hm = NULL;
272 }
273
274 static void hashmap_add(struct hashmap *hm, struct rule *r)
275 {
276         unsigned b = (r->hash & HASHMAP_MASK(hm));
277
278         while (hm->bucket[b]) {
279                 if (!hm->cmp(r, (struct rule *)hm->bucket[b]))
280                         break;
281                 b = (b + 1) & HASHMAP_MASK(hm);
282         }
283
284         hm->bucket[b] = r;
285 }
286
287 static struct rule *hashmap_search(struct hashmap *hm,
288                 const char *tag, char prio)
289 {
290         const unsigned key = util_hash_key(tag, prio);
291
292         unsigned b = (key & HASHMAP_MASK(hm));
293         unsigned b0 = b;
294
295         while (hm->bucket[b]) {
296                 if (!hm->match(hm->bucket[b], key, tag, prio))
297                         break;
298
299                 b = (b + 1) & HASHMAP_MASK(hm);
300
301                 assert(b0 != b);
302         }
303
304         if (!hm->bucket[b])
305                 return NULL;
306
307         return hm->bucket[b];
308 }
309
310 /* Must be always executed after __log_config_read() */
311 int __log_limiter_initialize(struct rule *rules_table)
312 {
313         int hm_size;
314         struct rule *rlist = NULL;
315
316         /* logconfig.c module had to initialize this correctly */
317         if (NULL == rules_table)
318                 return (-1);
319
320         /* Count rules in the table */
321         hm_size = 0;
322         rlist = rules_table;
323         while (rlist) {
324                 hm_size++;
325                 rlist = rlist->prev;
326         }
327
328         /* Allocate hashmap */
329         rules_hashmap = (struct hashmap *) hashmap_create(hm_size,
330                                                         &rule_compare,
331                                                         &rule_match);
332         if (NULL == rules_hashmap || !rules_hashmap->size) {
333                 hashmap_destroy(&rules_hashmap);
334                 return -1;
335         }
336
337         /* Add rule to hashmap */
338         rlist = rules_table;
339         while (rlist) {
340                 hashmap_add(rules_hashmap, rlist);
341                 rlist = rlist->prev;
342         }
343
344         return 0;
345 }
346
347 static void destroy_hashmap_etc(void)
348 {
349         hashmap_destroy(&rules_hashmap);
350         rules_destroy(&original_rules_table);
351 }
352
353 void __log_limiter_destroy(void)
354 {
355         cached_pid_rule = NULL;
356         list_clear_free_contents(&pid_rules);
357         destroy_hashmap_etc();
358 }
359
360 struct rule *__log_limiter_add_rule(struct rule **rules_table, const char *tag, int prio, int limit)
361 {
362         assert(tag);
363
364         struct rule *r;
365
366         for (struct rule *i = *rules_table; i; i = i->prev) {
367                 if (strcmp(i->tag, tag) || i->prio != util_prio_to_char(prio))
368                         continue;
369
370                 if (i->limit != limit) { //set type here only if the limit is updated
371                         i->limit = limit;
372                         i->type = RULE_DEFAULT;
373                 }
374                 return i;
375         }
376
377         r = (struct rule *) malloc(sizeof(*r));
378         if (NULL == r)
379                 return NULL;
380
381         memset(r, 0, sizeof(*r));
382
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);
386         r->limit = limit;
387         r->start = time(NULL);
388         r->hit = 0;
389         r->type = RULE_DEFAULT;
390
391         r->prev = *rules_table;
392         *rules_table = r;
393
394         return r;
395 }
396
397 struct limiter_limits __log_limiter_get_limits(const char *tag, int prio)
398 {
399         const struct rule fallback = { .limit = -1 };
400         const char prio_c = util_prio_to_char(prio);
401
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,
407         };
408 }
409
410
411 /* Function implement logic needed to decide,
412  * whenever message is written to log or not.
413  *
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.
416  */
417 struct pass_log_result __log_limiter_pass_log(const char *tag, int prio)
418 {
419         struct pass_log_result pid_result = block_by_pid();
420         if (pid_result.decision != DECISION_ALLOWED)
421                 return pid_result;
422
423         if (!rules_hashmap)
424                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
425
426         /* allow empty-tagged messages and make it easy to catch an application that does that */
427         if (!strlen(tag))
428                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
429
430         const char prio_c = util_prio_to_char(prio);
431         struct rule *r =
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, "*", '*');
436
437         if (!r)
438                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
439         if (!r->limit)
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, };
443
444         /* Decide, if it should go through or stop */
445         time_t now = time(NULL);
446
447         if (0 > now)
448                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
449
450         if (now - r->start <= refresh_rate_s) {
451                 if (r->hit >= 0) {
452                         if (r->hit < r->limit) {
453                                 r->hit++;
454                                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
455                         }
456                         r->hit = INT_MIN+1;
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,
461                         };
462                 } else {
463                         r->hit++;
464                         return (struct pass_log_result) { .decision = DECISION_DENIED, };
465                 }
466         } else {
467                 r->start = now;
468                 r->hit = 0;
469                 return (struct pass_log_result) { .decision = DECISION_ALLOWED, };
470         }
471 }
472
473 /**
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 *)
479  */
480 static void regular_limiter_iteration(const char *key, const char *value, void *userdata)
481 {
482         assert(key);
483         assert(value);
484         struct rule **rules_table = (struct rule **)userdata;
485         assert(rules_table);
486
487         static const int prefix_len = sizeof("limiter|") - 1;
488         char * delimiter_pos;
489         char limiter_tag[MAX_CONF_KEY_LEN];
490         int limit;
491
492         if (strncmp(key, "limiter|", prefix_len))
493                 return;
494
495         delimiter_pos = strchr(key + prefix_len + 1, '|');
496         if (!delimiter_pos || (delimiter_pos + 1 == key + strlen(key)))
497                 return;
498
499         snprintf(limiter_tag, delimiter_pos - (key + prefix_len) + 1, "%s", key + prefix_len);
500
501         if (!strcmp(value, "allow"))
502                 limit = __LOG_LIMITER_LIMIT_MAX + 1;
503         else if (!strcmp(value, "deny"))
504                 limit = 0;
505         else
506                 limit = atoi(value);
507
508         __log_limiter_add_rule(rules_table, limiter_tag, *(delimiter_pos + 1), limit);
509 }
510
511 static void pid_limiter_iteration(const char *key, const char *value, void *userdata)
512 {
513         if (strncmp(key, "pidlimit|", sizeof "pidlimit|" - 1))
514                 return;
515
516         int pid = atoi(key + sizeof "pidlimit|" - 1);
517
518         int limit;
519         if (strcmp(value, "deny") == 0)
520                 limit = 0;
521         else if (strcmp(value, "allow") == 0)
522                 limit = __LOG_LIMITER_LIMIT_MAX + 1;
523         else
524                 limit = atoi(value);
525
526         struct pid_limit *const p = malloc(sizeof *p);
527         if (!p)
528                 return;
529         p->limit = limit;
530         p->pid = pid;
531
532         list_add(&pid_rules, p);
533 }
534
535 static void __config_iteration(const char *key, const char *value, void *userdata)
536 {
537         regular_limiter_iteration(key, value, userdata);
538         pid_limiter_iteration(key, value, userdata);
539 }
540
541 int __log_limiter_create(const struct log_config *config)
542 {
543         assert(config);
544         assert(!original_rules_table);
545         assert(!rules_hashmap);
546
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);
551
552         const int r = __log_limiter_initialize(original_rules_table);
553         if (r) {
554                 rules_destroy(&original_rules_table);
555                 return pid_rules != NULL;
556         }
557
558         for (struct rule *i = original_rules_table; i; i = i->prev)
559                 i->type = RULE_STATIC;
560
561         return 1;
562 }
563
564 void __log_limiter_update(const struct log_config *config)
565 {
566         assert(config);
567         assert(!original_rules_table || rules_hashmap);
568
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);
572                 if (!r)
573                         continue;
574                 r->type = RULE_STATIC;
575         }
576
577         cached_pid_rule = NULL;
578         list_clear_free_contents(&pid_rules);
579         log_config_foreach(config, __config_iteration, &rules_table);
580
581         if (rules_hashmap) {
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);
585                         if (!prev_rule)
586                                 continue;
587
588                         i->hit = prev_rule->hit;
589                         i->start = prev_rule->start;
590                 }
591         }
592
593         hashmap_destroy(&rules_hashmap);
594         if (__log_limiter_initialize(rules_table) < 0) {
595                 destroy_hashmap_etc();
596                 return;
597         }
598
599         rules_destroy(&current_rules_table);
600         current_rules_table = rules_table;
601 }
602
603 /**
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
610  */
611 int __log_limiter_dump_rule(struct rule **r, char *buf, const size_t size)
612 {
613         struct rule *ruleptr = *r ? : current_rules_table;
614         int s = 0;
615
616         if (!ruleptr) {
617                 *r = NULL;
618                 return 0;
619         }
620
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 ");
625         else
626                 s = snprintf(buf, size, "%d logs/minute ", ruleptr->limit);
627
628         if (s < 0)
629                 return -1;
630
631         s = snprintf(buf + s, size - s, "for %s:%c (%s)",
632                         ruleptr->tag,
633                         util_prio_to_char(ruleptr->prio),
634                         (ruleptr->type == RULE_STATIC) ? "static" : "dynamic");
635         if (s < 0)
636                 return -1;
637
638         *r = ruleptr->prev;
639         return 0;
640 }
641
642 list_head __log_limiter_get_pid_limits(void)
643 {
644         return pid_rules;
645 }