mirror of https://github.com/asterisk/asterisk
				
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							325 lines
						
					
					
						
							7.7 KiB
						
					
					
				
			
		
		
	
	
							325 lines
						
					
					
						
							7.7 KiB
						
					
					
				| /*
 | |
|  * Asterisk -- An open source telephony toolkit.
 | |
|  *
 | |
|  * Copyright (C) 2020, Sangoma Technologies Corporation
 | |
|  *
 | |
|  * Kevin Harwell <kharwell@sangoma.com>
 | |
|  *
 | |
|  * See http://www.asterisk.org for more information about
 | |
|  * the Asterisk project. Please do not directly contact
 | |
|  * any of the maintainers of this project for assistance;
 | |
|  * the project provides a web site, mailing lists and IRC
 | |
|  * channels for your use.
 | |
|  *
 | |
|  * This program is free software, distributed under the terms of
 | |
|  * the GNU General Public License Version 2. See the LICENSE file
 | |
|  * at the top of the source tree.
 | |
|  */
 | |
| 
 | |
| #include "asterisk.h"
 | |
| 
 | |
| #include "asterisk/cli.h"
 | |
| #include "asterisk/conversions.h"
 | |
| #include "asterisk/logger_category.h"
 | |
| #include "asterisk/vector.h"
 | |
| 
 | |
| struct category_t {
 | |
| 	int sublevel;
 | |
| 	uintmax_t id;
 | |
| 	char name[0];
 | |
| };
 | |
| 
 | |
| AST_VECTOR_RW(categories_t, struct category_t *);
 | |
| 
 | |
| struct categories_level_t {
 | |
| 	int type;
 | |
| 	int sublevel;
 | |
| 	uintmax_t id_pool;
 | |
| 	uintmax_t state;
 | |
| 	struct categories_t categories;
 | |
| };
 | |
| 
 | |
| /*! \brief Retrieve the next available id.
 | |
|  *
 | |
|  * Ids must be a power of 2. This allows for fast lookup, and "or'ing" of ids
 | |
|  * in order to permit multiple categories in a log statement.
 | |
|  */
 | |
| static uintmax_t get_next_id(struct categories_level_t *level)
 | |
| {
 | |
| 	if (level->id_pool == 0) {
 | |
| 		level->id_pool = 1;
 | |
| 	} else if (level->id_pool >= (UINTMAX_MAX / 2)) {
 | |
| 		/* No more ids left*/
 | |
| 		return 0;
 | |
| 	} else {
 | |
| 		level->id_pool <<= 1;
 | |
| 	}
 | |
| 
 | |
| 	return level->id_pool;
 | |
| }
 | |
| 
 | |
| static int cmp_by_name(const struct category_t *category, const char *name)
 | |
| {
 | |
| 	return !strcmp(category->name, name);
 | |
| }
 | |
| 
 | |
| static uintmax_t category_register(struct categories_level_t *level, const char *name)
 | |
| {
 | |
| 	int i;
 | |
| 	struct category_t *category;
 | |
| 
 | |
| 	AST_VECTOR_RW_WRLOCK(&level->categories);
 | |
| 
 | |
| 	i = AST_VECTOR_GET_INDEX(&level->categories, name, cmp_by_name);
 | |
| 	if (i >= 0) {
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 		ast_log(LOG_ERROR, "Cannot register logger category '%s'. "
 | |
| 				"Name already used for type.\n", name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	category = ast_calloc(1, sizeof(*category) + strlen(name) + 1);
 | |
| 	if (!category) {
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	category->id = get_next_id(level);
 | |
| 	category->sublevel = AST_LOG_CATEGORY_DISABLED;
 | |
| 	strcpy(category->name, name); /* Safe */
 | |
| 
 | |
| 	if (AST_VECTOR_APPEND(&level->categories, category)) {
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 		ast_log(LOG_ERROR, "Cannot register logger category '%s'. "
 | |
| 				"Unable to append.\n", name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 	return category->id;
 | |
| }
 | |
| 
 | |
| static int category_unregister(struct categories_level_t *level, const char *name)
 | |
| {
 | |
| 	int res;
 | |
| 
 | |
| 	AST_VECTOR_RW_WRLOCK(&level->categories);
 | |
| 	res = AST_VECTOR_REMOVE_CMP_UNORDERED(&level->categories, name, cmp_by_name, ast_free);
 | |
| 	AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static int casecmp_by_name(const struct category_t *category, const char *name)
 | |
| {
 | |
| 	return !strcasecmp(category->name, name);
 | |
| }
 | |
| 
 | |
| static int category_set_sublevel(struct category_t *category, struct categories_level_t *level,
 | |
| 	const char *name, int sublevel)
 | |
| {
 | |
| 	int locked = 0;
 | |
| 
 | |
| 	if (!category) {
 | |
| 		struct category_t **obj;
 | |
| 
 | |
| 		if (!name) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		locked = !AST_VECTOR_RW_WRLOCK(&level->categories);
 | |
| 		if (!locked) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		obj = AST_VECTOR_GET_CMP(&level->categories, name, casecmp_by_name);
 | |
| 		if (!obj) {
 | |
| 			AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		category = *obj;
 | |
| 	}
 | |
| 
 | |
| 	category->sublevel = sublevel;
 | |
| 
 | |
| 	if (category->sublevel == AST_LOG_CATEGORY_DISABLED) {
 | |
| 		level->state &= ~category->id;
 | |
| 	} else {
 | |
| 		level->state |= category->id;
 | |
| 	}
 | |
| 
 | |
| 	if (locked) {
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int category_set_sublevels(struct categories_level_t *level,
 | |
| 	const char * const *names, size_t size, int default_sublevel)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	if (!names || !size) {
 | |
| 		level->state = default_sublevel;
 | |
| 		AST_VECTOR_RW_WRLOCK(&level->categories);
 | |
| 		AST_VECTOR_CALLBACK_VOID(&level->categories, category_set_sublevel,
 | |
| 			level, NULL, default_sublevel);
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < size; ++i) {
 | |
| 		const char *sublevel;
 | |
| 		int num = default_sublevel;
 | |
| 
 | |
| 		sublevel = strchr(names[i], ':');
 | |
| 		if (sublevel) {
 | |
| 			size_t len = ++sublevel - names[i];
 | |
| 			char name[len];
 | |
| 
 | |
| 			if (*sublevel && ast_str_to_int(sublevel, &num)) {
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			ast_copy_string(name, names[i], len);
 | |
| 			category_set_sublevel(NULL, level, name, num);
 | |
| 		} else {
 | |
| 			category_set_sublevel(NULL, level, names[i], num);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static char *category_complete(struct categories_level_t *level, const char * const *argv,
 | |
| 	int argc, const char *word, int state)
 | |
| {
 | |
| 	int wordlen = strlen(word);
 | |
| 	int which = 0;
 | |
| 	int i, j;
 | |
| 
 | |
| 	AST_VECTOR_RW_RDLOCK(&level->categories);
 | |
| 
 | |
| 	if (argc == AST_VECTOR_SIZE(&level->categories)) {
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < AST_VECTOR_SIZE(&level->categories); ++i) {
 | |
| 		struct category_t *category = AST_VECTOR_GET(&level->categories, i);
 | |
| 
 | |
| 		if (!strncasecmp(word, category->name, wordlen) && (++which > state)) {
 | |
| 			/* Check to see if one is already been included */
 | |
| 			for (j = 0; j < argc; ++j) {
 | |
| 				if (!strncasecmp(category->name, argv[j], strlen(category->name))) {
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if (j != argc) {
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			if (state != -1) {
 | |
| 				AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 				return ast_strdup(category->name);
 | |
| 			}
 | |
| 
 | |
| 			if (ast_cli_completion_add(ast_strdup(category->name))) {
 | |
| 				AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 				return NULL;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int category_is_allowed(int sublevel, struct categories_level_t *level, uintmax_t ids)
 | |
| {
 | |
| 	/* Check level, and potentially allow but only if there is a matching state enabled */
 | |
| 	if (ids & level->state) {
 | |
| 		int i;
 | |
| 
 | |
| 		if (sublevel == AST_LOG_CATEGORY_ENABLED || sublevel == 0) {
 | |
| 			/* If at least one id is enabled then always allow these sublevels */
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		AST_VECTOR_RW_RDLOCK(&level->categories);
 | |
| 		for (i = 0; i < AST_VECTOR_SIZE(&level->categories); ++i) {
 | |
| 			struct category_t *category = AST_VECTOR_GET(&level->categories, i);
 | |
| 
 | |
| 			/*
 | |
| 			 * If there is at least one matching category, and that category is enabled
 | |
| 			 * or its sub-level is at or above the given sub-level then allow.
 | |
| 			 */
 | |
| 			if ((category->id & ids) && category->sublevel != AST_LOG_CATEGORY_DISABLED &&
 | |
| 				(category->sublevel == AST_LOG_CATEGORY_ENABLED || category->sublevel >= sublevel)) {
 | |
| 				AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 				return 1;
 | |
| 			}
 | |
| 		}
 | |
| 		AST_VECTOR_RW_UNLOCK(&level->categories);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct categories_level_t debug_categories = {
 | |
| 	.type = __LOG_DEBUG,
 | |
| 	.sublevel = 0,
 | |
| 	.id_pool = 0,
 | |
| 	.state = 0,
 | |
| };
 | |
| 
 | |
| uintmax_t ast_debug_category_register(const char *name)
 | |
| {
 | |
| 	return category_register(&debug_categories, name);
 | |
| }
 | |
| 
 | |
| int ast_debug_category_unregister(const char *name)
 | |
| {
 | |
| 	return category_unregister(&debug_categories, name);
 | |
| }
 | |
| 
 | |
| int ast_debug_category_set_sublevel(const char *name, int sublevel)
 | |
| {
 | |
| 	return category_set_sublevel(NULL, &debug_categories, name, sublevel);
 | |
| }
 | |
| 
 | |
| int ast_debug_category_set_sublevels(const char * const *names,
 | |
| 	size_t size, int default_sublevel)
 | |
| {
 | |
| 	return category_set_sublevels(&debug_categories, names, size, default_sublevel);
 | |
| }
 | |
| 
 | |
| char *ast_debug_category_complete(const char * const *argv, int argc,
 | |
| 	const char *word, int state)
 | |
| {
 | |
| 	return category_complete(&debug_categories, argv, argc, word, state);
 | |
| }
 | |
| 
 | |
| int ast_debug_category_is_allowed(int sublevel, uintmax_t ids)
 | |
| {
 | |
| 	return category_is_allowed(sublevel, &debug_categories, ids);
 | |
| }
 | |
| 
 | |
| int ast_logger_category_unload(void)
 | |
| {
 | |
| 	AST_VECTOR_RW_FREE(&debug_categories.categories);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ast_logger_category_load(void)
 | |
| {
 | |
| 	if (AST_VECTOR_RW_INIT(&debug_categories.categories, 10)) {
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 |