mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-04 05:15:22 +00:00 
			
		
		
		
	ASTERISK_REGISTER_FILE no longer has any purpose so this commit removes all traces of it. Previously exported symbols removed: * __ast_register_file * __ast_unregister_file * ast_complete_source_filename This also removes the mtx_prof static variable that was declared when MTX_PROFILE was enabled. This variable was only used in lock.c so it is now initialized in that file only. ASTERISK-26480 #close Change-Id: I1074af07d71f9e159c48ef36631aa432c86f9966
		
			
				
	
	
		
			498 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			498 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Asterisk -- An open source telephony toolkit.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2013, Digium, Inc.
 | 
						|
 *
 | 
						|
 * Jonathan Rose <jrose@digium.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.
 | 
						|
 */
 | 
						|
 | 
						|
/*! \file
 | 
						|
 *
 | 
						|
 * \brief Channel Bridging Roles API
 | 
						|
 *
 | 
						|
 * \author Jonathan Rose <jrose@digium.com>
 | 
						|
 *
 | 
						|
 * \ingroup bridges
 | 
						|
 */
 | 
						|
 | 
						|
/*** MODULEINFO
 | 
						|
	<support_level>core</support_level>
 | 
						|
 ***/
 | 
						|
 | 
						|
#include "asterisk.h"
 | 
						|
 | 
						|
#include <signal.h>
 | 
						|
 | 
						|
#include "asterisk/logger.h"
 | 
						|
#include "asterisk/channel.h"
 | 
						|
#include "asterisk/datastore.h"
 | 
						|
#include "asterisk/linkedlists.h"
 | 
						|
#include "asterisk/bridge.h"
 | 
						|
#include "asterisk/bridge_roles.h"
 | 
						|
#include "asterisk/stringfields.h"
 | 
						|
 | 
						|
struct bridge_role_option {
 | 
						|
	AST_LIST_ENTRY(bridge_role_option) list;
 | 
						|
	AST_DECLARE_STRING_FIELDS(
 | 
						|
		AST_STRING_FIELD(option);
 | 
						|
		AST_STRING_FIELD(value);
 | 
						|
	);
 | 
						|
};
 | 
						|
 | 
						|
struct bridge_role {
 | 
						|
	AST_LIST_ENTRY(bridge_role) list;
 | 
						|
	AST_LIST_HEAD(, bridge_role_option) options;
 | 
						|
	char role[AST_ROLE_LEN];
 | 
						|
};
 | 
						|
 | 
						|
struct bridge_roles_datastore {
 | 
						|
	AST_LIST_HEAD(, bridge_role) role_list;
 | 
						|
};
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Destructor function for a bridge role
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param role bridge_role being destroyed
 | 
						|
 *
 | 
						|
 * \return Nothing
 | 
						|
 */
 | 
						|
static void bridge_role_destroy(struct bridge_role *role)
 | 
						|
{
 | 
						|
	struct bridge_role_option *role_option;
 | 
						|
	while ((role_option = AST_LIST_REMOVE_HEAD(&role->options, list))) {
 | 
						|
		ast_string_field_free_memory(role_option);
 | 
						|
		ast_free(role_option);
 | 
						|
	}
 | 
						|
	ast_free(role);
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Destructor function for bridge role datastores
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param data Pointer to the datastore being destroyed
 | 
						|
 *
 | 
						|
 * \return Nothing
 | 
						|
 */
 | 
						|
static void bridge_role_datastore_destroy(void *data)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore = data;
 | 
						|
	struct bridge_role *role;
 | 
						|
 | 
						|
	while ((role = AST_LIST_REMOVE_HEAD(&roles_datastore->role_list, list))) {
 | 
						|
		bridge_role_destroy(role);
 | 
						|
	}
 | 
						|
 | 
						|
	ast_free(roles_datastore);
 | 
						|
}
 | 
						|
 | 
						|
static const struct ast_datastore_info bridge_role_info = {
 | 
						|
	.type = "bridge roles",
 | 
						|
	.destroy = bridge_role_datastore_destroy,
 | 
						|
};
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Setup a bridge role datastore on a channel
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param chan Chan the datastore is being setup on
 | 
						|
 *
 | 
						|
 * \retval NULL if failed
 | 
						|
 * \retval pointer to the newly created datastore
 | 
						|
 */
 | 
						|
static struct bridge_roles_datastore *setup_bridge_roles_datastore(struct ast_channel *chan)
 | 
						|
{
 | 
						|
	struct ast_datastore *datastore = NULL;
 | 
						|
	struct bridge_roles_datastore *roles_datastore = NULL;
 | 
						|
 | 
						|
	if (!(datastore = ast_datastore_alloc(&bridge_role_info, NULL))) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(roles_datastore = ast_calloc(1, sizeof(*roles_datastore)))) {
 | 
						|
		ast_datastore_free(datastore);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	datastore->data = roles_datastore;
 | 
						|
	ast_channel_datastore_add(chan, datastore);
 | 
						|
	return roles_datastore;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Get the bridge_roles_datastore from a channel if it exists. Don't create one if it doesn't.
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param chan Channel we want the bridge_roles_datastore from
 | 
						|
 *
 | 
						|
 * \retval NULL if we can't find the datastore
 | 
						|
 * \retval pointer to the bridge_roles_datastore
 | 
						|
 */
 | 
						|
static struct bridge_roles_datastore *fetch_bridge_roles_datastore(struct ast_channel *chan)
 | 
						|
{
 | 
						|
	struct ast_datastore *datastore = NULL;
 | 
						|
 | 
						|
	ast_channel_lock(chan);
 | 
						|
	if (!(datastore = ast_channel_datastore_find(chan, &bridge_role_info, NULL))) {
 | 
						|
		ast_channel_unlock(chan);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	ast_channel_unlock(chan);
 | 
						|
 | 
						|
	return datastore->data;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Get the bridge_roles_datastore from a channel if it exists. If not, create one.
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param chan Channel we want the bridge_roles_datastore from
 | 
						|
 *
 | 
						|
 * \retval NULL If we can't find and can't create the datastore
 | 
						|
 * \retval pointer to the bridge_roles_datastore
 | 
						|
 */
 | 
						|
static struct bridge_roles_datastore *fetch_or_create_bridge_roles_datastore(struct ast_channel *chan)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore;
 | 
						|
 | 
						|
	ast_channel_lock(chan);
 | 
						|
	roles_datastore = fetch_bridge_roles_datastore(chan);
 | 
						|
	if (!roles_datastore) {
 | 
						|
		roles_datastore = setup_bridge_roles_datastore(chan);
 | 
						|
	}
 | 
						|
	ast_channel_unlock(chan);
 | 
						|
 | 
						|
	return roles_datastore;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Obtain a role from a bridge_roles_datastore if the datastore has it
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param roles_datastore The bridge_roles_datastore we are looking for the role of
 | 
						|
 * \param role_name Name of the role being sought
 | 
						|
 *
 | 
						|
 * \retval NULL if the datastore does not have the requested role
 | 
						|
 * \retval pointer to the requested role
 | 
						|
 */
 | 
						|
static struct bridge_role *get_role_from_datastore(struct bridge_roles_datastore *roles_datastore, const char *role_name)
 | 
						|
{
 | 
						|
	struct bridge_role *role;
 | 
						|
 | 
						|
	AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
 | 
						|
		if (!strcmp(role->role, role_name)) {
 | 
						|
			return role;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Obtain a role from a channel structure if the channel's datastore has it
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param channel The channel we are checking the role of
 | 
						|
 * \param role_name Name of the role sought
 | 
						|
 *
 | 
						|
 * \retval NULL if the channel's datastore does not have the requested role
 | 
						|
 * \retval pointer to the requested role
 | 
						|
 */
 | 
						|
static struct bridge_role *get_role_from_channel(struct ast_channel *channel, const char *role_name)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(channel);
 | 
						|
	return roles_datastore ? get_role_from_datastore(roles_datastore, role_name) : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Obtain a role option from a bridge role if it exists in the bridge role's option list
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param role a pointer to the bridge role wea re searching for the option of
 | 
						|
 * \param option Name of the option sought
 | 
						|
 *
 | 
						|
 * \retval NULL if the bridge role doesn't have the requested option
 | 
						|
 * \retval pointer to the requested option
 | 
						|
 */
 | 
						|
static struct bridge_role_option *get_role_option(struct bridge_role *role, const char *option)
 | 
						|
{
 | 
						|
	struct bridge_role_option *role_option = NULL;
 | 
						|
	AST_LIST_TRAVERSE(&role->options, role_option, list) {
 | 
						|
		if (!strcmp(role_option->option, option)) {
 | 
						|
			return role_option;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Setup a bridge role on an existing bridge role datastore
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param roles_datastore bridge_roles_datastore receiving the new role
 | 
						|
 * \param role_name Name of the role being received
 | 
						|
 *
 | 
						|
 * \retval 0 on success
 | 
						|
 * \retval -1 on failure
 | 
						|
 */
 | 
						|
static int setup_bridge_role(struct bridge_roles_datastore *roles_datastore, const char *role_name)
 | 
						|
{
 | 
						|
	struct bridge_role *role;
 | 
						|
	role = ast_calloc(1, sizeof(*role));
 | 
						|
 | 
						|
	if (!role) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	ast_copy_string(role->role, role_name, sizeof(role->role));
 | 
						|
 | 
						|
	AST_LIST_INSERT_TAIL(&roles_datastore->role_list, role, list);
 | 
						|
	ast_debug(3, "Set role '%s'\n", role_name);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*!
 | 
						|
 * \internal
 | 
						|
 * \brief Setup a bridge role option on an existing bridge role
 | 
						|
 * \since 12.0.0
 | 
						|
 *
 | 
						|
 * \param role The role receiving the option
 | 
						|
 * \param option Name of the option
 | 
						|
 * \param value the option's value
 | 
						|
 *
 | 
						|
 * \retval 0 on success
 | 
						|
 * \retval -1 on failure
 | 
						|
 */
 | 
						|
static int setup_bridge_role_option(struct bridge_role *role, const char *option, const char *value)
 | 
						|
{
 | 
						|
	struct bridge_role_option *role_option;
 | 
						|
 | 
						|
	if (!value) {
 | 
						|
		value = "";
 | 
						|
	}
 | 
						|
 | 
						|
	role_option = ast_calloc(1, sizeof(*role_option));
 | 
						|
	if (!role_option) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ast_string_field_init(role_option, 32)) {
 | 
						|
		ast_free(role_option);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	ast_string_field_set(role_option, option, option);
 | 
						|
	ast_string_field_set(role_option, value, value);
 | 
						|
 | 
						|
	AST_LIST_INSERT_TAIL(&role->options, role_option, list);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore = fetch_or_create_bridge_roles_datastore(chan);
 | 
						|
 | 
						|
	if (!roles_datastore) {
 | 
						|
		ast_log(LOG_WARNING, "Unable to set up bridge role datastore on channel %s\n", ast_channel_name(chan));
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Check to make sure we aren't adding a redundant role */
 | 
						|
	if (get_role_from_datastore(roles_datastore, role_name)) {
 | 
						|
		ast_debug(2, "Bridge role %s is already applied to the channel %s\n", role_name, ast_channel_name(chan));
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/* It wasn't already there, so we can just finish setting it up now. */
 | 
						|
	return setup_bridge_role(roles_datastore, role_name);
 | 
						|
}
 | 
						|
 | 
						|
void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
 | 
						|
	struct bridge_role *role;
 | 
						|
 | 
						|
	if (!roles_datastore) {
 | 
						|
		/* The roles datastore didn't already exist, so there is no need to remove a role */
 | 
						|
		ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
 | 
						|
		if (!strcmp(role->role, role_name)) {
 | 
						|
			ast_debug(2, "Removing bridge role %s from channel %s\n", role_name, ast_channel_name(chan));
 | 
						|
			AST_LIST_REMOVE_CURRENT(list);
 | 
						|
			bridge_role_destroy(role);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	AST_LIST_TRAVERSE_SAFE_END;
 | 
						|
 | 
						|
	ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
 | 
						|
}
 | 
						|
 | 
						|
void ast_channel_clear_bridge_roles(struct ast_channel *chan)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
 | 
						|
	struct bridge_role *role;
 | 
						|
 | 
						|
	if (!roles_datastore) {
 | 
						|
		/* The roles datastore didn't already exist, so there is no need to remove any roles */
 | 
						|
		ast_debug(2, "Roles did not exist on channel %s\n", ast_channel_name(chan));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
 | 
						|
		ast_debug(2, "Removing bridge role %s from channel %s\n", role->role, ast_channel_name(chan));
 | 
						|
		AST_LIST_REMOVE_CURRENT(list);
 | 
						|
		bridge_role_destroy(role);
 | 
						|
	}
 | 
						|
	AST_LIST_TRAVERSE_SAFE_END;
 | 
						|
}
 | 
						|
 | 
						|
int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
 | 
						|
{
 | 
						|
	struct bridge_role *role = get_role_from_channel(channel, role_name);
 | 
						|
	struct bridge_role_option *role_option;
 | 
						|
 | 
						|
	if (!role) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	role_option = get_role_option(role, option);
 | 
						|
 | 
						|
	if (role_option) {
 | 
						|
		ast_string_field_set(role_option, value, value);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return setup_bridge_role_option(role, option, value);
 | 
						|
}
 | 
						|
 | 
						|
int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
 | 
						|
{
 | 
						|
	return get_role_from_channel(channel, role_name) ? 1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
 | 
						|
{
 | 
						|
	struct bridge_role *role;
 | 
						|
	struct bridge_role_option *role_option;
 | 
						|
 | 
						|
	role = get_role_from_channel(channel, role_name);
 | 
						|
	if (!role) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	role_option = get_role_option(role, option);
 | 
						|
 | 
						|
	return role_option ? role_option->value : NULL;
 | 
						|
}
 | 
						|
 | 
						|
int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
 | 
						|
{
 | 
						|
	if (!bridge_channel->bridge_roles) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	return get_role_from_datastore(bridge_channel->bridge_roles, role_name) ? 1 : 0;
 | 
						|
}
 | 
						|
 | 
						|
const char *ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option)
 | 
						|
{
 | 
						|
	struct bridge_role *role;
 | 
						|
	struct bridge_role_option *role_option = NULL;
 | 
						|
 | 
						|
	if (!bridge_channel->bridge_roles) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	role = get_role_from_datastore(bridge_channel->bridge_roles, role_name);
 | 
						|
 | 
						|
	if (!role) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	role_option = get_role_option(role, option);
 | 
						|
 | 
						|
	return role_option ? role_option->value : NULL;
 | 
						|
}
 | 
						|
 | 
						|
int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel)
 | 
						|
{
 | 
						|
	struct bridge_roles_datastore *roles_datastore;
 | 
						|
	struct bridge_role *role = NULL;
 | 
						|
	struct bridge_role_option *role_option;
 | 
						|
 | 
						|
	if (!bridge_channel->chan) {
 | 
						|
		ast_debug(2, "Attempted to set roles on a bridge channel that has no associated channel. That's a bad idea.\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (bridge_channel->bridge_roles) {
 | 
						|
		ast_debug(2, "Attempted to reset roles while roles were already established. Purge existing roles first.\n");
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	roles_datastore = fetch_bridge_roles_datastore(bridge_channel->chan);
 | 
						|
	if (!roles_datastore) {
 | 
						|
		/* No roles to establish. */
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(bridge_channel->bridge_roles = ast_calloc(1, sizeof(*bridge_channel->bridge_roles)))) {
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
 | 
						|
		struct bridge_role *this_role_copy;
 | 
						|
 | 
						|
		if (setup_bridge_role(bridge_channel->bridge_roles, role->role)) {
 | 
						|
			/* We need to abandon the copy because we couldn't setup a role */
 | 
						|
			ast_bridge_channel_clear_roles(bridge_channel);
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		this_role_copy = AST_LIST_LAST(&bridge_channel->bridge_roles->role_list);
 | 
						|
 | 
						|
		AST_LIST_TRAVERSE(&role->options, role_option, list) {
 | 
						|
			if (setup_bridge_role_option(this_role_copy, role_option->option, role_option->value)) {
 | 
						|
				/* We need to abandon the copy because we couldn't setup a role option */
 | 
						|
				ast_bridge_channel_clear_roles(bridge_channel);
 | 
						|
				return -1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel)
 | 
						|
{
 | 
						|
	if (bridge_channel->bridge_roles) {
 | 
						|
		bridge_role_datastore_destroy(bridge_channel->bridge_roles);
 | 
						|
		bridge_channel->bridge_roles = NULL;
 | 
						|
	}
 | 
						|
}
 |