LOGGING: remove duplicated code between vty-logging and cfg-logging
[osmocom-bb.git] / src / vty / logging_vty.c
1 /* OpenBSC logging helper for the VTY */
2 /* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
3  * (C) 2009-2010 by Holger Hans Peter Freyther
4  * All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "../../config.h"
26
27 #include <osmocore/talloc.h>
28 #include <osmocore/logging.h>
29 #include <osmocore/utils.h>
30
31 //#include <openbsc/vty.h>
32
33 #include <osmocom/vty/command.h>
34 #include <osmocom/vty/buffer.h>
35 #include <osmocom/vty/vty.h>
36 #include <osmocom/vty/telnet_interface.h>
37 #include <osmocom/vty/logging.h>
38
39 #define LOG_STR "Configure logging sub-system\n"
40
41 extern const struct log_info *osmo_log_info;
42
43 static void _vty_output(struct log_target *tgt,
44                         unsigned int level, const char *line)
45 {
46         struct vty *vty = tgt->tgt_vty.vty;
47         vty_out(vty, "%s", line);
48         /* This is an ugly hack, but there is no easy way... */
49         if (strchr(line, '\n'))
50                 vty_out(vty, "\r");
51 }
52
53 struct log_target *log_target_create_vty(struct vty *vty)
54 {
55         struct log_target *target;
56
57         target = log_target_create();
58         if (!target)
59                 return NULL;
60
61         target->tgt_vty.vty = vty;
62         target->output = _vty_output;
63         return target;
64 }
65
66 DEFUN(enable_logging,
67       enable_logging_cmd,
68       "logging enable",
69         LOGGING_STR
70       "Enables logging to this vty\n")
71 {
72         struct telnet_connection *conn;
73
74         conn = (struct telnet_connection *) vty->priv;
75         if (conn->dbg) {
76                 vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
77                 return CMD_WARNING;
78         }
79
80         conn->dbg = log_target_create_vty(vty);
81         if (!conn->dbg)
82                 return CMD_WARNING;
83
84         log_add_target(conn->dbg);
85         return CMD_SUCCESS;
86 }
87
88 struct log_target *osmo_log_vty2tgt(struct vty *vty)
89 {
90         struct telnet_connection *conn;
91
92         if (vty->node == CFG_LOG_NODE)
93                 return vty->index;
94
95
96         conn = (struct telnet_connection *) vty->priv;
97         if (!conn->dbg)
98                 vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
99
100         return conn->dbg;
101 }
102
103 DEFUN(logging_fltr_all,
104       logging_fltr_all_cmd,
105       "logging filter all (0|1)",
106         LOGGING_STR FILTER_STR
107         "Do you want to log all messages?\n"
108         "Only print messages matched by other filters\n"
109         "Bypass filter and print all messages\n")
110 {
111         struct log_target *tgt = osmo_log_vty2tgt(vty);
112
113         if (!tgt)
114                 return CMD_WARNING;
115
116         log_set_all_filter(tgt, atoi(argv[0]));
117         return CMD_SUCCESS;
118 }
119
120 DEFUN(logging_use_clr,
121       logging_use_clr_cmd,
122       "logging color (0|1)",
123         LOGGING_STR "Configure color-printing for log messages\n"
124       "Don't use color for printing messages\n"
125       "Use color for printing messages\n")
126 {
127         struct log_target *tgt = osmo_log_vty2tgt(vty);
128
129         if (!tgt)
130                 return CMD_WARNING;
131
132         log_set_use_color(tgt, atoi(argv[0]));
133         return CMD_SUCCESS;
134 }
135
136 DEFUN(logging_prnt_timestamp,
137       logging_prnt_timestamp_cmd,
138       "logging timestamp (0|1)",
139         LOGGING_STR "Configure log message timestamping\n"
140         "Don't prefix each log message\n"
141         "Prefix each log message with current timestamp\n")
142 {
143         struct log_target *tgt = osmo_log_vty2tgt(vty);
144
145         if (!tgt)
146                 return CMD_WARNING;
147
148         log_set_print_timestamp(tgt, atoi(argv[0]));
149         return CMD_SUCCESS;
150 }
151
152 /* FIXME: those have to be kept in sync with the log levels and categories */
153 #define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs|ns|bssgp|llc|sndcp|isup|m2ua|pcap|nat|all)"
154 #define CATEGORIES_HELP \
155         "A-bis Radio Link Layer (RLL)\n"                        \
156         "Layer3 Call Control (CC)\n"                            \
157         "Layer3 Mobility Management (MM)\n"                     \
158         "Layer3 Radio Resource (RR)\n"                          \
159         "A-bis Radio Signalling Link (RSL)\n"                   \
160         "A-bis Network Management / O&M (NM/OML)\n"             \
161         "Layer3 Short Messagaging Service (SMS)\n"              \
162         "Paging Subsystem\n"                                    \
163         "MNCC API for Call Control application\n"               \
164         "A-bis Input Subsystem\n"                               \
165         "A-bis Input Driver for Signalling\n"                   \
166         "A-bis Input Driver for B-Channel (voice data)\n"       \
167         "A-bis B-Channel / Sub-channel Multiplexer\n"           \
168         "Radio Measurements\n"                                  \
169         "SCCP\n"                                                \
170         "Mobile Switching Center\n"                             \
171         "Media Gateway Control Protocol\n"                      \
172         "Hand-over\n"                                           \
173         "Database Layer\n"                                      \
174         "Reference Counting\n"                                  \
175         "GPRS Core\n"                                           \
176         "GPRS Network Service (NS)\n"                           \
177         "GPRS BSS Gateway Protocol (BSSGP)\n"                   \
178         "GPRS Logical Link Control Protocol (LLC)\n"            \
179         "GPRS Sub-Network Dependent Control Protocol (SNDCP)\n" \
180         "ISDN User Part (ISUP)\n"                               \
181         "SCTP M2UA\n"                                           \
182         "Trace message IO\n"                                    \
183         "BSC NAT\n"                                             \
184         "Global setting for all subsytems\n"
185
186 #define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)"
187 #define LEVELS_HELP     \
188         "Log simply everything\n"                               \
189         "Log debug messages and higher levels\n"                \
190         "Log informational messages and higher levels\n"        \
191         "Log noticable messages and higher levels\n"            \
192         "Log error messages and higher levels\n"                \
193         "Log only fatal messages\n"
194
195 DEFUN(logging_level,
196       logging_level_cmd,
197       "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS,
198       LOGGING_STR
199       "Set the log level for a specified category\n"
200       CATEGORIES_HELP
201       LEVELS_HELP)
202 {
203         int category = log_parse_category(argv[0]);
204         int level = log_parse_level(argv[1]);
205         struct log_target *tgt = osmo_log_vty2tgt(vty);
206
207         if (!tgt)
208                 return CMD_WARNING;
209
210         if (level < 0) {
211                 vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE);
212                 return CMD_WARNING;
213         }
214
215         /* Check for special case where we want to set global log level */
216         if (!strcmp(argv[0], "all")) {
217                 log_set_log_level(tgt, level);
218                 return CMD_SUCCESS;
219         }
220
221         if (category < 0) {
222                 vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE);
223                 return CMD_WARNING;
224         }
225
226         tgt->categories[category].enabled = 1;
227         tgt->categories[category].loglevel = level;
228
229         return CMD_SUCCESS;
230 }
231
232 DEFUN(logging_set_category_mask,
233       logging_set_category_mask_cmd,
234       "logging set log mask MASK",
235         LOGGING_STR
236       "Decide which categories to output.\n")
237 {
238         struct log_target *tgt = osmo_log_vty2tgt(vty);
239
240         if (!tgt)
241                 return CMD_WARNING;
242
243         log_parse_category_mask(tgt, argv[0]);
244         return CMD_SUCCESS;
245 }
246
247 DEFUN(diable_logging,
248       disable_logging_cmd,
249       "logging disable",
250         LOGGING_STR
251       "Disables logging to this vty\n")
252 {
253         struct log_target *tgt = osmo_log_vty2tgt(vty);
254         struct telnet_connection *conn = (struct telnet_connection *) vty->priv;
255
256         if (!tgt)
257                 return CMD_WARNING;
258
259         log_del_target(tgt);
260         talloc_free(tgt);
261         conn->dbg = NULL;
262
263         return CMD_SUCCESS;
264 }
265
266 static void vty_print_logtarget(struct vty *vty, const struct log_info *info,
267                                 const struct log_target *tgt)
268 {
269         unsigned int i;
270
271         vty_out(vty, " Global Loglevel: %s%s",
272                 log_level_str(tgt->loglevel), VTY_NEWLINE);
273         vty_out(vty, " Use color: %s, Print Timestamp: %s%s",
274                 tgt->use_color ? "On" : "Off",
275                 tgt->print_timestamp ? "On" : "Off", VTY_NEWLINE);
276
277         vty_out(vty, " Log Level specific information:%s", VTY_NEWLINE);
278
279         for (i = 0; i < info->num_cat; i++) {
280                 const struct log_category *cat = &tgt->categories[i];
281                 vty_out(vty, "  %-10s %-10s %-8s %s%s",
282                         info->cat[i].name+1, log_level_str(cat->loglevel),
283                         cat->enabled ? "Enabled" : "Disabled",
284                         info->cat[i].description,
285                         VTY_NEWLINE);
286         }
287 }
288
289 #define SHOW_LOG_STR "Show current logging configuration\n"
290
291 DEFUN(show_logging_vty,
292       show_logging_vty_cmd,
293       "show logging vty",
294         SHOW_STR SHOW_LOG_STR
295         "Show current logging configuration for this vty\n")
296 {
297         struct log_target *tgt = osmo_log_vty2tgt(vty);
298
299         if (!tgt)
300                 return CMD_WARNING;
301
302         vty_print_logtarget(vty, osmo_log_info, tgt);
303
304         return CMD_SUCCESS;
305 }
306
307 gDEFUN(cfg_description, cfg_description_cmd,
308         "description .TEXT",
309         "Save human-readable decription of the object\n")
310 {
311         char **dptr = vty->index_sub;
312
313         if (!dptr) {
314                 vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
315                 return CMD_WARNING;
316         }
317
318         *dptr = argv_concat(argv, argc, 0);
319         if (!dptr)
320                 return CMD_WARNING;
321
322         return CMD_SUCCESS;
323 }
324
325 gDEFUN(cfg_no_description, cfg_no_description_cmd,
326         "no description",
327         NO_STR
328         "Remove description of the object\n")
329 {
330         char **dptr = vty->index_sub;
331
332         if (!dptr) {
333                 vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE);
334                 return CMD_WARNING;
335         }
336
337         if (*dptr) {
338                 talloc_free(*dptr);
339                 *dptr = NULL;
340         }
341
342         return CMD_SUCCESS;
343 }
344
345 /* Support for configuration of log targets != the current vty */
346
347 struct cmd_node cfg_log_node = {
348         CFG_LOG_NODE,
349         "%s(config-log)# ",
350         1
351 };
352
353 #ifdef HAVE_SYSLOG_H
354
355 #include <syslog.h>
356
357 static const int local_sysl_map[] = {
358         [0] = LOG_LOCAL0,
359         [1] = LOG_LOCAL1,
360         [2] = LOG_LOCAL2,
361         [3] = LOG_LOCAL3,
362         [4] = LOG_LOCAL4,
363         [5] = LOG_LOCAL5,
364         [6] = LOG_LOCAL6,
365         [7] = LOG_LOCAL7
366 };
367
368 static int _cfg_log_syslog(struct vty *vty, int facility)
369 {
370         struct log_target *tgt;
371
372         /* First delete the old syslog target, if any */
373         tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL);
374         if (tgt)
375                 log_target_destroy(tgt);
376
377         tgt = log_target_create_syslog("FIXME", 0, facility);
378         if (!tgt) {
379                 vty_out(vty, "%% Unable to open syslog%s", VTY_NEWLINE);
380                 return CMD_WARNING;
381         }
382         log_add_target(tgt);
383
384         vty->index = tgt;
385         vty->node = CFG_LOG_NODE;
386
387         return CMD_SUCCESS;
388 }
389
390 DEFUN(cfg_log_syslog_local, cfg_log_syslog_local_cmd,
391       "log syslog local <0-7>",
392         LOG_STR "Logging via syslog\n" "Syslog LOCAL facility\n"
393         "Local facility number\n")
394 {
395         int local = atoi(argv[0]);
396         int facility = local_sysl_map[local];
397
398         return _cfg_log_syslog(vty, facility);
399 }
400
401 static struct value_string sysl_level_names[] = {
402         { LOG_AUTHPRIV, "authpriv" },
403         { LOG_CRON,     "cron" },
404         { LOG_DAEMON,   "daemon" },
405         { LOG_FTP,      "ftp" },
406         { LOG_LPR,      "lpr" },
407         { LOG_MAIL,     "mail" },
408         { LOG_NEWS,     "news" },
409         { LOG_USER,     "user" },
410         { LOG_UUCP,     "uucp" },
411         /* only for value -> string conversion */
412         { LOG_LOCAL0,   "local 0" },
413         { LOG_LOCAL1,   "local 1" },
414         { LOG_LOCAL2,   "local 2" },
415         { LOG_LOCAL3,   "local 3" },
416         { LOG_LOCAL4,   "local 4" },
417         { LOG_LOCAL5,   "local 5" },
418         { LOG_LOCAL6,   "local 6" },
419         { LOG_LOCAL7,   "local 7" },
420         { 0, NULL }
421 };
422
423 DEFUN(cfg_log_syslog, cfg_log_syslog_cmd,
424       "log syslog (authpriv|cron|daemon|ftp|lpr|mail|news|user|uucp)",
425         LOG_STR "Logging via syslog\n")
426 {
427         int facility = get_string_value(sysl_level_names, argv[0]);
428
429         return _cfg_log_syslog(vty, facility);
430 }
431
432 DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd,
433         "no log syslog",
434         NO_STR LOG_STR "Logging via syslog\n")
435 {
436         struct log_target *tgt;
437
438         tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL);
439         if (!tgt) {
440                 vty_out(vty, "%% No syslog target found%s",
441                         VTY_NEWLINE);
442                 return CMD_WARNING;
443         }
444
445         log_target_destroy(tgt);
446
447         return CMD_SUCCESS;
448 }
449 #endif /* HAVE_SYSLOG_H */
450
451 DEFUN(cfg_log_stderr, cfg_log_stderr_cmd,
452         "log stderr",
453         LOG_STR "Logging via STDERR of the process\n")
454 {
455         struct log_target *tgt;
456
457         tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL);
458         if (!tgt) {
459                 tgt = log_target_create_stderr();
460                 if (!tgt) {
461                         vty_out(vty, "%% Unable to create stderr log%s",
462                                 VTY_NEWLINE);
463                         return CMD_WARNING;
464                 }
465                 log_add_target(tgt);
466         }
467
468         vty->index = tgt;
469         vty->node = CFG_LOG_NODE;
470
471         return CMD_SUCCESS;
472 }
473
474 DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd,
475         "no log stderr",
476         NO_STR LOG_STR "Logging via STDERR of the process\n")
477 {
478         struct log_target *tgt;
479
480         tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL);
481         if (!tgt) {
482                 vty_out(vty, "%% No stderr logging active%s", VTY_NEWLINE);
483                 return CMD_WARNING;
484         }
485
486         log_target_destroy(tgt);
487
488         return CMD_SUCCESS;
489 }
490
491 DEFUN(cfg_log_file, cfg_log_file_cmd,
492         "log file .FILENAME",
493         LOG_STR "Logging to text file\n" "Filename\n")
494 {
495         const char *fname = argv[0];
496         struct log_target *tgt;
497
498         tgt = log_target_find(LOG_TGT_TYPE_FILE, fname);
499         if (!tgt) {
500                 tgt = log_target_create_file(fname);
501                 if (!tgt) {
502                         vty_out(vty, "%% Unable to create file `%s'%s",
503                                 fname, VTY_NEWLINE);
504                         return CMD_WARNING;
505                 }
506                 log_add_target(tgt);
507         }
508
509         vty->index = tgt;
510         vty->node = CFG_LOG_NODE;
511
512         return CMD_SUCCESS;
513 }
514
515
516 DEFUN(cfg_no_log_file, cfg_no_log_file_cmd,
517         "no log file .FILENAME",
518         NO_STR LOG_STR "Logging to text file\n" "Filename\n")
519 {
520         const char *fname = argv[0];
521         struct log_target *tgt;
522
523         tgt = log_target_find(LOG_TGT_TYPE_FILE, fname);
524         if (!tgt) {
525                 vty_out(vty, "%% No such log file `%s'%s",
526                         fname, VTY_NEWLINE);
527                 return CMD_WARNING;
528         }
529
530         log_target_destroy(tgt);
531
532         return CMD_SUCCESS;
533 }
534
535 static int config_write_log_single(struct vty *vty, struct log_target *tgt)
536 {
537         int i;
538         char level_lower[32];
539
540         switch (tgt->type) {
541         case LOG_TGT_TYPE_VTY:
542                 return 1;
543                 break;
544         case LOG_TGT_TYPE_STDERR:
545                 vty_out(vty, "log stderr%s", VTY_NEWLINE);
546                 break;
547         case LOG_TGT_TYPE_SYSLOG:
548 #ifdef HAVE_SYSLOG_H
549                 vty_out(vty, "log syslog %s%s",
550                         get_value_string(sysl_level_names,
551                                          tgt->tgt_syslog.facility),
552                         VTY_NEWLINE);
553 #endif
554                 break;
555         case LOG_TGT_TYPE_FILE:
556                 vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE);
557                 break;
558         }
559
560         vty_out(vty, "  logging color %u%s", tgt->use_color ? 1 : 0,
561                 VTY_NEWLINE);
562         vty_out(vty, "  logging timestamp %u%s", tgt->print_timestamp ? 1 : 0,
563                 VTY_NEWLINE);
564
565         /* stupid old osmo logging API uses uppercase strings... */
566         osmo_str2lower(level_lower, log_level_str(tgt->loglevel));
567         vty_out(vty, "  logging level all %s%s", level_lower, VTY_NEWLINE);
568
569         for (i = 0; i < osmo_log_info->num_cat; i++) {
570                 const struct log_category *cat = &tgt->categories[i];
571                 char cat_lower[32];
572
573                 /* stupid old osmo logging API uses uppercase strings... */
574                 osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1);
575                 osmo_str2lower(level_lower, log_level_str(cat->loglevel));
576
577                 vty_out(vty, "  logging level %s %s%s", cat_lower, level_lower,
578                         VTY_NEWLINE);
579         }
580
581         /* FIXME: levels */
582
583         return 1;
584 }
585
586 static int config_write_log(struct vty *vty)
587 {
588         struct log_target *dbg = vty->index;
589
590         llist_for_each_entry(dbg, &osmo_log_target_list, entry)
591                 config_write_log_single(vty, dbg);
592
593         return 1;
594 }
595
596 void logging_vty_add_cmds()
597 {
598         install_element_ve(&enable_logging_cmd);
599         install_element_ve(&disable_logging_cmd);
600         install_element_ve(&logging_fltr_all_cmd);
601         install_element_ve(&logging_use_clr_cmd);
602         install_element_ve(&logging_prnt_timestamp_cmd);
603         install_element_ve(&logging_set_category_mask_cmd);
604         install_element_ve(&logging_level_cmd);
605         install_element_ve(&show_logging_vty_cmd);
606
607         install_node(&cfg_log_node, config_write_log);
608         install_element(CFG_LOG_NODE, &logging_fltr_all_cmd);
609         install_element(CFG_LOG_NODE, &logging_use_clr_cmd);
610         install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd);
611         install_element(CFG_LOG_NODE, &logging_level_cmd);
612
613         install_element(CONFIG_NODE, &cfg_log_stderr_cmd);
614         install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd);
615         install_element(CONFIG_NODE, &cfg_log_file_cmd);
616         install_element(CONFIG_NODE, &cfg_no_log_file_cmd);
617 #ifdef HAVE_SYSLOG_H
618         install_element(CONFIG_NODE, &cfg_log_syslog_cmd);
619         install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd);
620         install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd);
621 #endif
622 }