diff options
| author | Harald Welte <laforge@gnumonks.org> | 2011-02-18 20:37:04 +0100 | 
|---|---|---|
| committer | Harald Welte <laforge@gnumonks.org> | 2011-02-18 20:37:04 +0100 | 
| commit | 2822296ddb22389c5b4e9b8fb6ef7c906ff99314 (patch) | |
| tree | 5a05ad56e7ac5730bb98d12eef4aa527e4c7d0d9 | |
| parent | 46cfd77f7521ad53934e1e42057723b4cd52d241 (diff) | |
LOGGING: configure logging from the vty
We can now configure logging to (multiple) files, stderr and syslog
from the vty command line in a persistent way (config file)
| -rw-r--r-- | configure.in | 2 | ||||
| -rw-r--r-- | include/osmocom/vty/command.h | 1 | ||||
| -rw-r--r-- | include/osmocore/logging.h | 13 | ||||
| -rw-r--r-- | include/osmocore/utils.h | 3 | ||||
| -rw-r--r-- | src/logging.c | 24 | ||||
| -rw-r--r-- | src/logging_syslog.c | 6 | ||||
| -rw-r--r-- | src/utils.c | 22 | ||||
| -rw-r--r-- | src/vty/Makefile.am | 2 | ||||
| -rw-r--r-- | src/vty/command.c | 4 | ||||
| -rw-r--r-- | src/vty/logging_vty.c | 377 | ||||
| -rw-r--r-- | src/vty/vty.c | 6 | 
11 files changed, 433 insertions, 27 deletions
| diff --git a/configure.in b/configure.in index 309aa034..165cecb7 100644 --- a/configure.in +++ b/configure.in @@ -18,7 +18,7 @@ AC_CONFIG_MACRO_DIR([m4])  dnl checks for header files  AC_HEADER_STDC -AC_CHECK_HEADERS(execinfo.h sys/select.h) +AC_CHECK_HEADERS(execinfo.h sys/select.h syslog.h ctype.h)  # The following test is taken from WebKit's webkit.m4  saved_CFLAGS="$CFLAGS" diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h index 69e9e772..caf04142 100644 --- a/include/osmocom/vty/command.h +++ b/include/osmocom/vty/command.h @@ -70,6 +70,7 @@ enum node_type {  	CONFIG_NODE,		/* Config node. Default mode of config file. */  	SERVICE_NODE,		/* Service node. */  	DEBUG_NODE,		/* Debug node. */ +	CFG_LOG_NODE,		/* Configure the logging */  	VTY_NODE,		/* Vty node. */ diff --git a/include/osmocore/logging.h b/include/osmocore/logging.h index d4d632d8..a2c63e9a 100644 --- a/include/osmocore/logging.h +++ b/include/osmocore/logging.h @@ -69,6 +69,13 @@ struct log_info {  	unsigned int num_cat;  }; +enum log_target_type { +	LOG_TGT_TYPE_VTY, +	LOG_TGT_TYPE_SYSLOG, +	LOG_TGT_TYPE_FILE, +	LOG_TGT_TYPE_STDERR, +}; +  struct log_target {          struct llist_head entry; @@ -80,6 +87,8 @@ struct log_target {  	int use_color:1;  	int print_timestamp:1; +	enum log_target_type type; +  	union {  		struct {  			FILE *out; @@ -88,6 +97,7 @@ struct log_target {  		struct {  			int priority; +			int facility;  		} tgt_syslog;  		struct { @@ -138,4 +148,7 @@ void log_del_target(struct log_target *target);  const char *log_vty_category_string(struct log_info *info);  const char *log_vty_level_string(struct log_info *info); +struct log_target *log_target_find(int type, const char *fname); +extern struct llist_head osmo_log_target_list; +  #endif /* _OSMOCORE_LOGGING_H */ diff --git a/include/osmocore/utils.h b/include/osmocore/utils.h index 6fe1b388..0cdf03b8 100644 --- a/include/osmocore/utils.h +++ b/include/osmocore/utils.h @@ -24,4 +24,7 @@ char *ubit_dump(const uint8_t *bits, unsigned int len);  #define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; +void osmo_str2lower(char *out, const char *in); +void osmo_str2upper(char *out, const char *in); +  #endif diff --git a/src/logging.c b/src/logging.c index 44528620..876a3526 100644 --- a/src/logging.c +++ b/src/logging.c @@ -41,7 +41,7 @@ const struct log_info *osmo_log_info;  static struct log_context log_context;  static void *tall_log_ctx = NULL; -static LLIST_HEAD(target_list); +LLIST_HEAD(osmo_log_target_list);  static const struct value_string loglevel_strs[] = {  	{ 0,		"EVERYTHING" }, @@ -176,7 +176,7 @@ static void _logp(unsigned int subsys, int level, char *file, int line,  {  	struct log_target *tar; -	llist_for_each_entry(tar, &target_list, entry) { +	llist_for_each_entry(tar, &osmo_log_target_list, entry) {  		struct log_category *category;  		int output = 0; @@ -239,7 +239,7 @@ void logp2(unsigned int subsys, unsigned int level, char *file, int line, int co  void log_add_target(struct log_target *target)  { -	llist_add_tail(&target->entry, &target_list); +	llist_add_tail(&target->entry, &osmo_log_target_list);  }  void log_del_target(struct log_target *target) @@ -338,6 +338,7 @@ struct log_target *log_target_create_stderr(void)  	if (!target)  		return NULL; +	target->type = LOG_TGT_TYPE_STDERR;  	target->tgt_file.out = stderr;  	target->output = _file_output;  	return target; @@ -354,6 +355,7 @@ struct log_target *log_target_create_file(const char *fname)  	if (!target)  		return NULL; +	target->type = LOG_TGT_TYPE_FILE;  	target->tgt_file.out = fopen(fname, "a");  	if (!target->tgt_file.out)  		return NULL; @@ -365,6 +367,22 @@ struct log_target *log_target_create_file(const char *fname)  	return target;  } +struct log_target *log_target_find(int type, const char *fname) +{ +	struct log_target *tgt; + +	llist_for_each_entry(tgt, &osmo_log_target_list, entry) { +		if (tgt->type != type) +			continue; +		if (tgt->type == LOG_TGT_TYPE_FILE) { +			if (!strcmp(fname, tgt->tgt_file.fname)) +				return tgt; +		} else +			return tgt; +	} +	return NULL; +} +  void log_target_destroy(struct log_target *target)  { diff --git a/src/logging_syslog.c b/src/logging_syslog.c index b65e8196..b558fc0b 100644 --- a/src/logging_syslog.c +++ b/src/logging_syslog.c @@ -21,6 +21,8 @@  #include "../config.h" +#ifdef HAVE_SYSLOG_H +  #include <stdarg.h>  #include <stdlib.h>  #include <stdio.h> @@ -64,9 +66,13 @@ struct log_target *log_target_create_syslog(const char *ident, int option,  	if (!target)  		return NULL; +	target->tgt_syslog.facility = facility; +	target->type = LOG_TGT_TYPE_SYSLOG;  	target->output = _syslog_output;  	openlog(ident, option, facility);  	return target;  } + +#endif /* HAVE_SYSLOG_H */ diff --git a/src/utils.c b/src/utils.c index 49c210e0..354fce57 100644 --- a/src/utils.c +++ b/src/utils.c @@ -134,3 +134,25 @@ char *hexdump_nospc(const unsigned char *buf, int len)  {  	return _hexdump(buf, len, "");  } + +#include "../config.h" +#ifdef HAVE_CTYPE_H +#include <ctype.h> +void osmo_str2lower(char *out, const char *in) +{ +	unsigned int i; + +	for (i = 0; i < strlen(in); i++) +		out[i] = tolower(in[i]); +	out[strlen(in)] = '\0'; +} + +void osmo_str2upper(char *out, const char *in) +{ +	unsigned int i; + +	for (i = 0; i < strlen(in); i++) +		out[i] = toupper(in[i]); +	out[strlen(in)] = '\0'; +} +#endif /* HAVE_CTYPE_H */ diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index 7353ab84..8d730c3c 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -10,5 +10,5 @@ lib_LTLIBRARIES = libosmovty.la  libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \  			telnet_interface.c logging_vty.c -libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la +libosmovty_la_LIBADD = -losmocore $(top_builddir)/src/libosmocore.la  endif diff --git a/src/vty/command.c b/src/vty/command.c index 7525df65..0f65224f 100644 --- a/src/vty/command.c +++ b/src/vty/command.c @@ -2180,6 +2180,9 @@ gDEFUN(config_exit,  	case VTY_NODE:  		vty->node = CONFIG_NODE;  		break; +	case CFG_LOG_NODE: +		vty->node = CONFIG_NODE; +		break;  	default:  		break;  	} @@ -2195,6 +2198,7 @@ gDEFUN(config_exit,  	case ENABLE_NODE:  		/* Nothing to do. */  		break; +	case CFG_LOG_NODE:  	case CONFIG_NODE:  	case VTY_NODE:  		vty_config_unlock(vty); diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index 55882a77..b51be7d6 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -22,8 +22,11 @@  #include <stdlib.h>  #include <string.h> +#include "../../config.h" +  #include <osmocore/talloc.h>  #include <osmocore/logging.h> +#include <osmocore/utils.h>  //#include <openbsc/vty.h> @@ -33,6 +36,8 @@  #include <osmocom/vty/telnet_interface.h>  #include <osmocom/vty/logging.h> +#define LOG_STR "Configure logging sub-system\n" +  extern const struct log_info *osmo_log_info;  static void _vty_output(struct log_target *tgt, @@ -179,46 +184,54 @@ DEFUN(logging_prnt_timestamp,  	"Log noticable messages and higher levels\n"		\  	"Log error messages and higher levels\n"		\  	"Log only fatal messages\n" -DEFUN(logging_level, -      logging_level_cmd, -      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, -      LOGGING_STR -      "Set the log level for a specified category\n" -      CATEGORIES_HELP -      LEVELS_HELP) -{ -	struct telnet_connection *conn; -	int category = log_parse_category(argv[0]); -	int level = log_parse_level(argv[1]); -	conn = (struct telnet_connection *) vty->priv; -	if (!conn->dbg) { -		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); -		return CMD_WARNING; -	} +static int _logging_level(struct vty *vty, struct log_target *dbg, +			  const char *cat_str, const char *lvl_str) +{ +	int category = log_parse_category(cat_str); +	int level = log_parse_level(lvl_str);  	if (level < 0) { -		vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); +		vty_out(vty, "Invalid level `%s'%s", lvl_str, VTY_NEWLINE);  		return CMD_WARNING;  	}  	/* Check for special case where we want to set global log level */ -	if (!strcmp(argv[0], "all")) { -		log_set_log_level(conn->dbg, level); +	if (!strcmp(cat_str, "all")) { +		log_set_log_level(dbg, level);  		return CMD_SUCCESS;  	}  	if (category < 0) { -		vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); +		vty_out(vty, "Invalid category `%s'%s", cat_str, VTY_NEWLINE);  		return CMD_WARNING;  	} -	conn->dbg->categories[category].enabled = 1; -	conn->dbg->categories[category].loglevel = level; +	dbg->categories[category].enabled = 1; +	dbg->categories[category].loglevel = level;  	return CMD_SUCCESS;  } +DEFUN(logging_level, +      logging_level_cmd, +      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, +      LOGGING_STR +      "Set the log level for a specified category\n" +      CATEGORIES_HELP +      LEVELS_HELP) +{ +	struct telnet_connection *conn; + +	conn = (struct telnet_connection *) vty->priv; +	if (!conn->dbg) { +		vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); +		return CMD_WARNING; +	} + +	return _logging_level(vty, conn->dbg, argv[0], argv[1]); +} +  DEFUN(logging_set_category_mask,        logging_set_category_mask_cmd,        "logging set log mask MASK", @@ -338,6 +351,310 @@ gDEFUN(cfg_no_description, cfg_no_description_cmd,  	return CMD_SUCCESS;  } +/* Support for configuration of log targets != the current vty */ + +struct cmd_node cfg_log_node = { +	CFG_LOG_NODE, +	"%s(config-log)# ", +	1 +}; + +DEFUN(cfg_log_fltr_all, +      cfg_log_fltr_all_cmd, +      "logging filter all (0|1)", +	LOGGING_STR FILTER_STR +	"Do you want to log all messages?\n" +	"Only print messages matched by other filters\n" +	"Bypass filter and print all messages\n") +{ +	struct log_target *dbg = vty->index; + +	log_set_all_filter(dbg, atoi(argv[0])); +	return CMD_SUCCESS; +} + +DEFUN(cfg_log_use_clr, +      cfg_log_use_clr_cmd, +      "logging color (0|1)", +	LOGGING_STR "Configure color-printing for log messages\n" +      "Don't use color for printing messages\n" +      "Use color for printing messages\n") +{ +	struct log_target *dbg = vty->index; + +	log_set_use_color(dbg, atoi(argv[0])); +	return CMD_SUCCESS; +} + +DEFUN(cfg_log_timestamp, +      cfg_log_timestamp_cmd, +      "logging timestamp (0|1)", +	LOGGING_STR "Configure log message timestamping\n" +	"Don't prefix each log message\n" +	"Prefix each log message with current timestamp\n") +{ +	struct log_target *dbg = vty->index; + +	log_set_print_timestamp(dbg, atoi(argv[0])); +	return CMD_SUCCESS; +} + +DEFUN(cfg_log_level, +      cfg_log_level_cmd, +      "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, +      LOGGING_STR +      "Set the log level for a specified category\n" +      CATEGORIES_HELP +      LEVELS_HELP) +{ +	struct log_target *dbg = vty->index; + +	return _logging_level(vty, dbg, argv[0], argv[1]); +} + +#ifdef HAVE_SYSLOG_H + +#include <syslog.h> + +static const int local_sysl_map[] = { +	[0] = LOG_LOCAL0, +	[1] = LOG_LOCAL1, +	[2] = LOG_LOCAL2, +	[3] = LOG_LOCAL3, +	[4] = LOG_LOCAL4, +	[5] = LOG_LOCAL5, +	[6] = LOG_LOCAL6, +	[7] = LOG_LOCAL7 +}; + +static int _cfg_log_syslog(struct vty *vty, int facility) +{ +	struct log_target *tgt; + +	/* First delete the old syslog target, if any */ +	tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL); +	if (tgt) +		log_target_destroy(tgt); + +	tgt = log_target_create_syslog("FIXME", 0, facility); +	if (!tgt) { +		vty_out(vty, "%% Unable to open syslog%s", VTY_NEWLINE); +		return CMD_WARNING; +	} +	log_add_target(tgt); + +	vty->index = tgt; +	vty->node = CFG_LOG_NODE; + +	return CMD_SUCCESS; +} + +DEFUN(cfg_log_syslog_local, cfg_log_syslog_local_cmd, +      "log syslog local <0-7>", +	LOG_STR "Logging via syslog\n" "Syslog LOCAL facility\n" +	"Local facility number\n") +{ +	int local = atoi(argv[0]); +	int facility = local_sysl_map[local]; + +	return _cfg_log_syslog(vty, facility); +} + +static struct value_string sysl_level_names[] = { +	{ LOG_AUTHPRIV, "authpriv" }, +	{ LOG_CRON, 	"cron" }, +	{ LOG_DAEMON,	"daemon" }, +	{ LOG_FTP,	"ftp" }, +	{ LOG_LPR,	"lpr" }, +	{ LOG_MAIL,	"mail" }, +	{ LOG_NEWS,	"news" }, +	{ LOG_USER,	"user" }, +	{ LOG_UUCP,	"uucp" }, +	/* only for value -> string conversion */ +	{ LOG_LOCAL0,	"local 0" }, +	{ LOG_LOCAL1,	"local 1" }, +	{ LOG_LOCAL2,	"local 2" }, +	{ LOG_LOCAL3,	"local 3" }, +	{ LOG_LOCAL4,	"local 4" }, +	{ LOG_LOCAL5,	"local 5" }, +	{ LOG_LOCAL6,	"local 6" }, +	{ LOG_LOCAL7,	"local 7" }, +	{ 0, NULL } +}; + +DEFUN(cfg_log_syslog, cfg_log_syslog_cmd, +      "log syslog (authpriv|cron|daemon|ftp|lpr|mail|news|user|uucp)", +	LOG_STR "Logging via syslog\n") +{ +	int facility = get_string_value(sysl_level_names, argv[0]); + +	return _cfg_log_syslog(vty, facility); +} + +DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd, +	"no log syslog", +	NO_STR LOG_STR "Logging via syslog\n") +{ +	struct log_target *tgt; + +	tgt = log_target_find(LOG_TGT_TYPE_SYSLOG, NULL); +	if (!tgt) { +		vty_out(vty, "%% No syslog target found%s", +			VTY_NEWLINE); +		return CMD_WARNING; +	} + +	log_target_destroy(tgt); + +	return CMD_SUCCESS; +} +#endif /* HAVE_SYSLOG_H */ + +DEFUN(cfg_log_stderr, cfg_log_stderr_cmd, +	"log stderr", +	LOG_STR "Logging via STDERR of the process\n") +{ +	struct log_target *tgt; + +	tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL); +	if (!tgt) { +		tgt = log_target_create_stderr(); +		if (!tgt) { +			vty_out(vty, "%% Unable to create stderr log%s", +				VTY_NEWLINE); +			return CMD_WARNING; +		} +		log_add_target(tgt); +	} + +	vty->index = tgt; +	vty->node = CFG_LOG_NODE; + +	return CMD_SUCCESS; +} + +DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd, +	"no log stderr", +	NO_STR LOG_STR "Logging via STDERR of the process\n") +{ +	struct log_target *tgt; + +	tgt = log_target_find(LOG_TGT_TYPE_STDERR, NULL); +	if (!tgt) { +		vty_out(vty, "%% No stderr logging active%s", VTY_NEWLINE); +		return CMD_WARNING; +	} + +	log_target_destroy(tgt); + +	return CMD_SUCCESS; +} + +DEFUN(cfg_log_file, cfg_log_file_cmd, +	"log file .FILENAME", +	LOG_STR "Logging to text file\n" "Filename\n") +{ +	const char *fname = argv[0]; +	struct log_target *tgt; + +	tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); +	if (!tgt) { +		tgt = log_target_create_file(fname); +		if (!tgt) { +			vty_out(vty, "%% Unable to create file `%s'%s", +				fname, VTY_NEWLINE); +			return CMD_WARNING; +		} +		log_add_target(tgt); +	} + +	vty->index = tgt; +	vty->node = CFG_LOG_NODE; + +	return CMD_SUCCESS; +} + + +DEFUN(cfg_no_log_file, cfg_no_log_file_cmd, +	"no log file .FILENAME", +	NO_STR LOG_STR "Logging to text file\n" "Filename\n") +{ +	const char *fname = argv[0]; +	struct log_target *tgt; + +	tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); +	if (!tgt) { +		vty_out(vty, "%% No such log file `%s'%s", +			fname, VTY_NEWLINE); +		return CMD_WARNING; +	} + +	log_target_destroy(tgt); + +	return CMD_SUCCESS; +} + +static int config_write_log_single(struct vty *vty, struct log_target *tgt) +{ +	int i; +	char level_lower[32]; + +	switch (tgt->type) { +	case LOG_TGT_TYPE_VTY: +		return 1; +		break; +	case LOG_TGT_TYPE_STDERR: +		vty_out(vty, "log stderr%s", VTY_NEWLINE); +		break; +	case LOG_TGT_TYPE_SYSLOG: +#ifdef HAVE_SYSLOG_H +		vty_out(vty, "log syslog %s%s", +			get_value_string(sysl_level_names, +					 tgt->tgt_syslog.facility), +			VTY_NEWLINE); +#endif +		break; +	case LOG_TGT_TYPE_FILE: +		vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE); +		break; +	} + +	vty_out(vty, "  logging color %u%s", tgt->use_color ? 1 : 0, +		VTY_NEWLINE); +	vty_out(vty, "  logging timestamp %u%s", tgt->print_timestamp ? 1 : 0, +		VTY_NEWLINE); + +	/* stupid old osmo logging API uses uppercase strings... */ +	osmo_str2lower(level_lower, log_level_str(tgt->loglevel)); +	vty_out(vty, "  logging level all %s%s", level_lower, VTY_NEWLINE); + +	for (i = 0; i < osmo_log_info->num_cat; i++) { +		const struct log_category *cat = &tgt->categories[i]; +		char cat_lower[32]; + +		/* stupid old osmo logging API uses uppercase strings... */ +		osmo_str2lower(cat_lower, osmo_log_info->cat[i].name+1); +		osmo_str2lower(level_lower, log_level_str(cat->loglevel)); + +		vty_out(vty, "  logging level %s %s%s", cat_lower, level_lower, +			VTY_NEWLINE); +	} + +	/* FIXME: levels */ + +	return 1; +} + +static int config_write_log(struct vty *vty) +{ +	struct log_target *dbg = vty->index; + +	llist_for_each_entry(dbg, &osmo_log_target_list, entry) +		config_write_log_single(vty, dbg); + +	return 1; +} +  void logging_vty_add_cmds()  {  	install_element_ve(&enable_logging_cmd); @@ -348,4 +665,20 @@ void logging_vty_add_cmds()  	install_element_ve(&logging_set_category_mask_cmd);  	install_element_ve(&logging_level_cmd);  	install_element_ve(&show_logging_vty_cmd); + +	install_node(&cfg_log_node, config_write_log); +	install_element(CFG_LOG_NODE, &cfg_log_fltr_all_cmd); +	install_element(CFG_LOG_NODE, &cfg_log_use_clr_cmd); +	install_element(CFG_LOG_NODE, &cfg_log_timestamp_cmd); +	install_element(CFG_LOG_NODE, &cfg_log_level_cmd); + +	install_element(CONFIG_NODE, &cfg_log_stderr_cmd); +	install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd); +	install_element(CONFIG_NODE, &cfg_log_file_cmd); +	install_element(CONFIG_NODE, &cfg_no_log_file_cmd); +#ifdef HAVE_SYSLOG_H +	install_element(CONFIG_NODE, &cfg_log_syslog_cmd); +	install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd); +	install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd); +#endif  } diff --git a/src/vty/vty.c b/src/vty/vty.c index a5b16dce..c1a9b3af 100644 --- a/src/vty/vty.c +++ b/src/vty/vty.c @@ -765,6 +765,9 @@ static void vty_end_config(struct vty *vty)  		vty_config_unlock(vty);  		vty->node = ENABLE_NODE;  		break; +	case CFG_LOG_NODE: +		vty->node = CONFIG_NODE; +		break;  	default:  		/* Unknown node, we have to ignore it. */  		break; @@ -1129,6 +1132,9 @@ static void vty_stop_input(struct vty *vty)  		vty_config_unlock(vty);  		vty->node = ENABLE_NODE;  		break; +	case CFG_LOG_NODE: +		vty->node = CONFIG_NODE; +		break;  	default:  		/* Unknown node, we have to ignore it. */  		break; | 
