mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +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;
							 | 
						||
| 
								 | 
							
								}
							 |