mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 18:55:19 +00:00 
			
		
		
		
	
		
			
	
	
		
			325 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			325 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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; | ||
|  | } |