log: Validate the size of vec in vlib_log
[vpp.git] / src / vlib / log.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdbool.h>
17 #include <vlib/vlib.h>
18 #include <vlib/log.h>
19 #include <syslog.h>
20
21 typedef struct
22 {
23   vlib_log_level_t level;
24   vlib_log_class_t class;
25   f64 timestamp;
26   u8 *string;
27 } vlib_log_entry_t;
28
29 typedef struct
30 {
31   u32 index;
32   u8 *name;
33   // level of log messages kept for this subclass
34   vlib_log_level_t level;
35   // level of log messages sent to syslog for this subclass
36   vlib_log_level_t syslog_level;
37   // flag saying whether this subclass is logged to syslog
38   f64 last_event_timestamp;
39   int last_sec_count;
40   int is_throttling;
41   int rate_limit;
42 } vlib_log_subclass_data_t;
43
44 typedef struct
45 {
46   u32 index;
47   u8 *name;
48   vlib_log_subclass_data_t *subclasses;
49 } vlib_log_class_data_t;
50
51 typedef struct
52 {
53   vlib_log_entry_t *entries;
54   vlib_log_class_data_t *classes;
55   int size, next, count;
56
57   /* our own log class */
58   vlib_log_class_t log_class;
59
60   int default_rate_limit;
61   int default_log_level;
62   int default_syslog_log_level;
63   int unthrottle_time;
64   u32 indent;
65 } vlib_log_main_t;
66
67 vlib_log_main_t log_main = {
68   .default_log_level = VLIB_LOG_LEVEL_NOTICE,
69   .default_syslog_log_level = VLIB_LOG_LEVEL_WARNING,
70   .unthrottle_time = 3,
71   .size = 512,
72   .default_rate_limit = 50,
73 };
74
75 static int
76 last_log_entry ()
77 {
78   vlib_log_main_t *lm = &log_main;
79   int i;
80
81   i = lm->next - lm->count;
82
83   if (i < 0)
84     i += lm->size;
85   return i;
86 }
87
88 static vlib_log_class_data_t *
89 get_class_data (vlib_log_class_t ci)
90 {
91   vlib_log_main_t *lm = &log_main;
92   return vec_elt_at_index (lm->classes, (ci >> 16));
93 }
94
95 static vlib_log_subclass_data_t *
96 get_subclass_data (vlib_log_class_t ci)
97 {
98   vlib_log_class_data_t *c = get_class_data (ci);
99   return vec_elt_at_index (c->subclasses, (ci & 0xffff));
100 }
101
102 static int
103 vlib_log_level_to_syslog_priority (vlib_log_level_t level)
104 {
105   switch (level)
106     {
107 #define LOG_DISABLED LOG_DEBUG
108 #define _(n,uc,lc) \
109     case VLIB_LOG_LEVEL_##uc:\
110       return LOG_##uc;
111       foreach_vlib_log_level
112 #undef _
113 #undef LOG_DISABLED
114     }
115   return LOG_DEBUG;
116 }
117
118 u8 *
119 format_vlib_log_class (u8 * s, va_list * args)
120 {
121   vlib_log_class_t ci = va_arg (*args, vlib_log_class_t);
122   vlib_log_class_data_t *c = get_class_data (ci);
123   vlib_log_subclass_data_t *sc = get_subclass_data (ci);
124
125   if (sc->name)
126     return format (s, "%v/%v", c->name, sc->name);
127   else
128     return format (s, "%v", c->name, 0);
129 }
130
131
132 void
133 vlib_log (vlib_log_level_t level, vlib_log_class_t class, char *fmt, ...)
134 {
135   vlib_main_t *vm = vlib_get_main ();
136   vlib_log_main_t *lm = &log_main;
137   vlib_log_entry_t *e;
138   vlib_log_subclass_data_t *sc = get_subclass_data (class);
139   va_list va;
140   f64 t = vlib_time_now (vm);
141   f64 delta = t - sc->last_event_timestamp;
142   u8 *s = 0;
143   bool use_formatted_log_entry = true;
144
145   vec_validate (lm->entries, lm->size);
146   /* make sure we are running on the main thread to avoid use in dataplane
147      code, for dataplane logging consider use of event-logger */
148   ASSERT (vlib_get_thread_index () == 0);
149
150   if (level > sc->level)
151     {
152       use_formatted_log_entry = false;
153       goto syslog;
154     }
155
156   if ((delta > lm->unthrottle_time) ||
157       (sc->is_throttling == 0 && (delta > 1)))
158     {
159       sc->last_event_timestamp = t;
160       sc->last_sec_count = 0;
161       sc->is_throttling = 0;
162     }
163   else
164     {
165       sc->last_sec_count++;
166       if (sc->last_sec_count > sc->rate_limit)
167         return;
168       else if (sc->last_sec_count == sc->rate_limit)
169         {
170           vec_reset_length (s);
171           s = format (0, "--- message(s) throttled ---");
172           sc->is_throttling = 1;
173         }
174     }
175
176   if (s == 0)
177     {
178       va_start (va, fmt);
179       s = va_format (s, fmt, &va);
180       va_end (va);
181     }
182
183   e = vec_elt_at_index (lm->entries, lm->next);
184   vec_free (e->string);
185   e->level = level;
186   e->class = class;
187   e->string = s;
188   e->timestamp = t;
189
190   lm->next = (lm->next + 1) % lm->size;
191   if (lm->size > lm->count)
192     lm->count++;
193
194 syslog:
195   if (sc->syslog_level != VLIB_LOG_LEVEL_DISABLED &&
196       level <= sc->syslog_level)
197     {
198       u8 *tmp = format (NULL, "%U", format_vlib_log_class, class);
199       if (use_formatted_log_entry)
200         {
201           syslog (vlib_log_level_to_syslog_priority (level), "%.*s: %.*s",
202                   vec_len (tmp), tmp,
203                   vec_len (s) - (vec_c_string_is_terminated (s) ? 1 : 0), s);
204         }
205       else
206         {
207           tmp = format (tmp, ": ");
208           va_start (va, fmt);
209           tmp = va_format (tmp, fmt, &va);
210           va_end (va);
211           syslog (vlib_log_level_to_syslog_priority (level), "%.*s",
212                   vec_len (tmp) - (vec_c_string_is_terminated (tmp) ? 1 : 0),
213                   tmp);
214         }
215       vec_free (tmp);
216     }
217
218 }
219
220 vlib_log_class_t
221 vlib_log_register_class (char *class, char *subclass)
222 {
223   vlib_log_main_t *lm = &log_main;
224   vlib_log_class_data_t *c = NULL;
225   vlib_log_subclass_data_t *s;
226   vlib_log_class_data_t *tmp;
227   vec_foreach (tmp, lm->classes)
228   {
229     if (!memcmp (class, tmp->name, vec_len (tmp->name)))
230       {
231         c = tmp;
232         break;
233       }
234   }
235   if (!c)
236     {
237       vec_add2 (lm->classes, c, 1);
238       c->index = c - lm->classes;
239       c->name = format (0, "%s", class);
240     }
241
242   vec_add2 (c->subclasses, s, 1);
243   s->index = s - c->subclasses;
244   s->name = subclass ? format (0, "%s", subclass) : 0;
245   s->rate_limit = lm->default_rate_limit;
246   s->level = lm->default_log_level;
247   s->syslog_level = lm->default_syslog_log_level;
248   return (c->index << 16) | (s->index);
249 }
250
251 u8 *
252 format_vlib_log_level (u8 * s, va_list * args)
253 {
254   vlib_log_level_t i = va_arg (*args, vlib_log_level_t);
255   char *t = 0;
256
257   switch (i)
258     {
259 #define _(v,uc,lc) case VLIB_LOG_LEVEL_##uc: t = #lc; break;
260       foreach_vlib_log_level
261 #undef _
262     default:
263       return format (s, "unknown");
264     }
265   return format (s, "%s", t);
266 }
267
268 u32
269 vlib_log_get_indent ()
270 {
271   return log_main.indent;
272 }
273
274 static clib_error_t *
275 vlib_log_init (vlib_main_t * vm)
276 {
277   vlib_log_main_t *lm = &log_main;
278   vec_validate (lm->entries, lm->size);
279   lm->log_class = vlib_log_register_class ("log", 0);
280   u8 *tmp = format (NULL, "%U %-10U %-10U ", format_time_float, 0, (f64) 0,
281                     format_white_space, 255, format_white_space, 255);
282   log_main.indent = vec_len (tmp);
283   vec_free (tmp);
284   return 0;
285 }
286
287 VLIB_INIT_FUNCTION (vlib_log_init);
288
289
290 static clib_error_t *
291 show_log (vlib_main_t * vm,
292           unformat_input_t * input, vlib_cli_command_t * cmd)
293 {
294   clib_error_t *error = 0;
295   vlib_log_main_t *lm = &log_main;
296   vlib_log_entry_t *e;
297   int i = last_log_entry ();
298   int count = lm->count;
299
300   while (count--)
301     {
302       e = vec_elt_at_index (lm->entries, i);
303       vlib_cli_output (vm, "%U %-10U %-10U %v",
304                        format_time_float, 0, e->timestamp,
305                        format_vlib_log_level, e->level,
306                        format_vlib_log_class, e->class, e->string);
307       i = (i + 1) % lm->size;
308     }
309
310   return error;
311 }
312
313 /* *INDENT-OFF* */
314 VLIB_CLI_COMMAND (cli_show_log, static) = {
315   .path = "show logging",
316   .short_help = "show logging",
317   .function = show_log,
318 };
319 /* *INDENT-ON* */
320
321 static clib_error_t *
322 show_log_config (vlib_main_t * vm,
323                  unformat_input_t * input, vlib_cli_command_t * cmd)
324 {
325   clib_error_t *error = 0;
326   vlib_log_main_t *lm = &log_main;
327   vlib_log_class_data_t *c;
328   vlib_log_subclass_data_t *sc;
329
330   vlib_cli_output (vm, "%-20s %u entries", "Buffer Size:", lm->size);
331   vlib_cli_output (vm, "Defaults:\n");
332   vlib_cli_output (vm, "%-20s %U", "  Log Level:",
333                    format_vlib_log_level, lm->default_log_level);
334   vlib_cli_output (vm, "%-20s %U", "  Syslog Log Level:",
335                    format_vlib_log_level, lm->default_syslog_log_level);
336   vlib_cli_output (vm, "%-20s %u msgs/sec", "  Rate Limit:",
337                    lm->default_rate_limit);
338   vlib_cli_output (vm, "\n");
339   vlib_cli_output (vm, "%-22s %-14s %-14s %s",
340                    "Class/Subclass", "Level", "Syslog Level", "Rate Limit");
341
342   vec_foreach (c, lm->classes)
343   {
344     vlib_cli_output (vm, "%s", c->name);
345     vec_foreach (sc, c->subclasses)
346     {
347       vlib_cli_output (vm, "  %-20s %-14U %-14U %d",
348                        sc->name ? (char *) sc->name : "default",
349                        format_vlib_log_level, sc->level,
350                        format_vlib_log_level, sc->syslog_level,
351                        sc->rate_limit);
352     }
353   }
354
355   return error;
356 }
357
358 /* *INDENT-OFF* */
359 VLIB_CLI_COMMAND (cli_show_log_config, static) = {
360   .path = "show logging configuration",
361   .short_help = "show logging configuration",
362   .function = show_log_config,
363 };
364 /* *INDENT-ON* */
365
366 static clib_error_t *
367 clear_log (vlib_main_t * vm,
368            unformat_input_t * input, vlib_cli_command_t * cmd)
369 {
370   clib_error_t *error = 0;
371   vlib_log_main_t *lm = &log_main;
372   vlib_log_entry_t *e;
373   int i = last_log_entry ();
374   int count = lm->count;
375
376   while (count--)
377     {
378       e = vec_elt_at_index (lm->entries, i);
379       vec_free (e->string);
380       i = (i + 1) % lm->size;
381     }
382
383   lm->count = 0;
384   lm->next = 0;
385   vlib_log_info (lm->log_class, "log cleared");
386   return error;
387 }
388
389 /* *INDENT-OFF* */
390 VLIB_CLI_COMMAND (cli_clear_log, static) = {
391   .path = "clear logging",
392   .short_help = "clear logging",
393   .function = clear_log,
394 };
395 /* *INDENT-ON* */
396
397 static uword
398 unformat_vlib_log_level (unformat_input_t * input, va_list * args)
399 {
400   vlib_log_level_t *level = va_arg (*args, vlib_log_level_t *);
401   u8 *level_str = NULL;
402   uword rv = 1;
403   if (unformat (input, "%s", &level_str))
404     {
405 #define _(v, uc, lc)                                   \
406   const char __##uc[] = #lc;                           \
407   if (!strcmp ((const char *) level_str, __##uc))       \
408     {                                                  \
409       *level = VLIB_LOG_LEVEL_##uc;                 \
410       rv = 1;                                          \
411       goto done;                                       \
412     }
413       foreach_vlib_log_level;
414       rv = 0;
415 #undef _
416     }
417 done:
418   vec_free (level_str);
419   return rv;
420 }
421
422 static uword
423 unformat_vlib_log_class (unformat_input_t * input, va_list * args)
424 {
425   vlib_log_class_data_t **class = va_arg (*args, vlib_log_class_data_t **);
426   uword rv = 0;
427   u8 *class_str = NULL;
428   vlib_log_main_t *lm = &log_main;
429   if (unformat (input, "%v", &class_str))
430     {
431       vlib_log_class_data_t *cdata;
432       vec_foreach (cdata, lm->classes)
433       {
434         if (vec_is_equal (cdata->name, class_str))
435           {
436             *class = cdata;
437             rv = 1;
438             break;
439           }
440       }
441     }
442   vec_free (class_str);
443   return rv;
444 }
445
446 static clib_error_t *
447 set_log_class (vlib_main_t * vm,
448                unformat_input_t * input, vlib_cli_command_t * cmd)
449 {
450   unformat_input_t _line_input, *line_input = &_line_input;
451   clib_error_t *rv = NULL;
452   int rate_limit;
453   bool set_rate_limit = false;
454   bool set_level = false;
455   bool set_syslog_level = false;
456   vlib_log_level_t level;
457   vlib_log_level_t syslog_level;
458
459   /* Get a line of input. */
460   if (!unformat_user (input, unformat_line_input, line_input))
461     return 0;
462
463   vlib_log_class_data_t *class = NULL;
464   if (!unformat (line_input, "%U", unformat_vlib_log_class, &class))
465     {
466       return clib_error_return (0, "unknown log class `%U'",
467                                 format_unformat_error, line_input);
468     }
469   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
470     {
471       if (unformat (line_input, "rate-limit %d", &rate_limit))
472         {
473           set_rate_limit = true;
474         }
475       else
476         if (unformat
477             (line_input, "level %U", unformat_vlib_log_level, &level))
478         {
479           set_level = true;
480         }
481       else
482         if (unformat
483             (line_input, "syslog-level %U", unformat_vlib_log_level,
484              &syslog_level))
485         {
486           set_syslog_level = true;
487         }
488       else
489         {
490           return clib_error_return (0, "unknown input `%U'",
491                                     format_unformat_error, line_input);
492         }
493     }
494
495   if (set_level)
496     {
497       vlib_log_subclass_data_t *subclass;
498       vec_foreach (subclass, class->subclasses)
499       {
500         subclass->level = level;
501       }
502     }
503   if (set_syslog_level)
504     {
505       vlib_log_subclass_data_t *subclass;
506       vec_foreach (subclass, class->subclasses)
507       {
508         subclass->syslog_level = syslog_level;
509       }
510     }
511   if (set_rate_limit)
512     {
513       vlib_log_subclass_data_t *subclass;
514       vec_foreach (subclass, class->subclasses)
515       {
516         subclass->rate_limit = rate_limit;
517       }
518     }
519
520   return rv;
521 }
522
523 /* *INDENT-OFF* */
524 VLIB_CLI_COMMAND (cli_set_log, static) = {
525   .path = "set logging class",
526   .short_help = "set loggging class <class> [rate-limit <int>] "
527     "[level <level>] [syslog-level <level>]",
528   .function = set_log_class,
529 };
530 /* *INDENT-ON* */
531
532 static clib_error_t *
533 set_log_unth_time (vlib_main_t * vm,
534                    unformat_input_t * input, vlib_cli_command_t * cmd)
535 {
536   unformat_input_t _line_input, *line_input = &_line_input;
537   clib_error_t *rv = NULL;
538   int unthrottle_time;
539   vlib_log_main_t *lm = &log_main;
540
541   /* Get a line of input. */
542   if (!unformat_user (input, unformat_line_input, line_input))
543     return 0;
544
545   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
546     {
547       if (unformat (line_input, "%d", &unthrottle_time))
548         lm->unthrottle_time = unthrottle_time;
549       else
550         return clib_error_return (0, "unknown input `%U'",
551                                   format_unformat_error, line_input);
552     }
553
554   return rv;
555 }
556
557 /* *INDENT-OFF* */
558 VLIB_CLI_COMMAND (cli_set_log_params, static) = {
559   .path = "set logging unthrottle-time",
560   .short_help = "set logging unthrottle-time <int>",
561   .function = set_log_unth_time,
562 };
563 /* *INDENT-ON* */
564
565 static clib_error_t *
566 set_log_size (vlib_main_t * vm,
567               unformat_input_t * input, vlib_cli_command_t * cmd)
568 {
569   unformat_input_t _line_input, *line_input = &_line_input;
570   clib_error_t *rv = NULL;
571   int size;
572   vlib_log_main_t *lm = &log_main;
573
574   /* Get a line of input. */
575   if (!unformat_user (input, unformat_line_input, line_input))
576     return 0;
577
578   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
579     {
580       if (unformat (line_input, "%d", &size))
581         {
582           lm->size = size;
583           vec_validate (lm->entries, lm->size);
584         }
585       else
586         return clib_error_return (0, "unknown input `%U'",
587                                   format_unformat_error, line_input);
588     }
589
590   return rv;
591 }
592
593 /* *INDENT-OFF* */
594 VLIB_CLI_COMMAND (cli_set_log_size, static) = {
595   .path = "set logging size",
596   .short_help = "set logging size <int>",
597   .function = set_log_size,
598 };
599 /* *INDENT-ON* */
600
601 static uword
602 unformat_vlib_log_subclass (unformat_input_t * input, va_list * args)
603 {
604   vlib_log_class_data_t *class = va_arg (*args, vlib_log_class_data_t *);
605   vlib_log_subclass_data_t **subclass =
606     va_arg (*args, vlib_log_subclass_data_t **);
607   uword rv = 0;
608   u8 *subclass_str = NULL;
609   if (unformat (input, "%v", &subclass_str))
610     {
611       vlib_log_subclass_data_t *scdata;
612       vec_foreach (scdata, class->subclasses)
613       {
614         if (vec_is_equal (scdata->name, subclass_str))
615           {
616             rv = 1;
617             *subclass = scdata;
618             break;
619           }
620       }
621     }
622   vec_free (subclass_str);
623   return rv;
624 }
625
626 static clib_error_t *
627 test_log_class_subclass (vlib_main_t * vm,
628                          unformat_input_t * input, vlib_cli_command_t * cmd)
629 {
630   unformat_input_t _line_input, *line_input = &_line_input;
631   /* Get a line of input. */
632   if (!unformat_user (input, unformat_line_input, line_input))
633     return 0;
634
635   vlib_log_class_data_t *class = NULL;
636   vlib_log_subclass_data_t *subclass = NULL;
637   vlib_log_level_t level;
638   if (unformat (line_input, "%U", unformat_vlib_log_level, &level))
639     {
640       if (unformat (line_input, "%U", unformat_vlib_log_class, &class))
641         {
642           if (unformat
643               (line_input, "%U", unformat_vlib_log_subclass, class,
644                &subclass))
645             {
646               vlib_log (level,
647                         (class->index << 16) | (subclass->index), "%U",
648                         format_unformat_input, line_input);
649             }
650           else
651             {
652               return clib_error_return (0,
653                                         "unknown log subclass near beginning of `%U'",
654                                         format_unformat_error, line_input);
655             }
656         }
657       else
658         {
659           return clib_error_return (0,
660                                     "unknown log class near beginning of `%U'",
661                                     format_unformat_error, line_input);
662         }
663     }
664   else
665     {
666       return clib_error_return (0, "unknown log level near beginning of `%U'",
667                                 format_unformat_error, line_input);
668     }
669   return 0;
670 }
671
672 /* *INDENT-OFF* */
673 VLIB_CLI_COMMAND (cli_test_log, static) = {
674   .path = "test log",
675   .short_help = "test log <class> <subclass> <level> <message",
676   .function = test_log_class_subclass,
677 };
678 /* *INDENT-ON* */
679
680 static clib_error_t *
681 log_config (vlib_main_t * vm, unformat_input_t * input)
682 {
683   vlib_log_main_t *lm = &log_main;
684
685   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
686     {
687       if (unformat (input, "size %d", &lm->size))
688         vec_validate (lm->entries, lm->size);
689       else if (unformat (input, "unthrottle-time %d", &lm->unthrottle_time))
690         ;
691       else if (unformat (input, "default-log-level %U",
692                          unformat_vlib_log_level, &lm->default_log_level))
693         ;
694       else if (unformat (input, "default-syslog-log-level %U",
695                          unformat_vlib_log_level,
696                          &lm->default_syslog_log_level))
697         ;
698       else
699         {
700           return unformat_parse_error (input);
701         }
702     }
703
704   return 0;
705 }
706
707 VLIB_EARLY_CONFIG_FUNCTION (log_config, "logging");
708
709 /*
710  * fd.io coding-style-patch-verification: ON
711  *
712  * Local Variables:
713  * eval: (c-set-style "gnu")
714  * End:
715  */