2009-03-05 18:18:27 +00:00
/*
* Asterisk - - An open source telephony toolkit .
*
* Copyright ( C ) 2007 - 2009 , Digium , Inc .
*
* Joshua Colp < jcolp @ 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
*
2013-07-24 15:38:18 +00:00
* \ brief Bridging API
2009-03-05 18:18:27 +00:00
*
* \ author Joshua Colp < jcolp @ digium . com >
*/
2012-06-15 16:20:16 +00:00
/*** MODULEINFO
< support_level > core < / support_level >
* * */
2013-11-01 21:51:20 +00:00
/*** DOCUMENTATION
< manager name = " BridgeTechnologyList " language = " en_US " >
< synopsis >
List available bridging technologies and their statuses .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< / syntax >
< description >
< para > Returns detailed information about the available bridging technologies . < / para >
< / description >
2016-08-14 01:15:58 +00:00
< see - also >
< ref type = " manager " > BridgeTechnologySuspend < / ref >
< ref type = " manager " > BridgeTechnologyUnsuspend < / ref >
< / see - also >
2013-11-01 21:51:20 +00:00
< / manager >
< manager name = " BridgeTechnologySuspend " language = " en_US " >
< synopsis >
Suspend a bridging technology .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " BridgeTechnology " required = " true " >
< para > The name of the bridging technology to suspend . < / para >
< / parameter >
< / syntax >
< description >
< para > Marks a bridging technology as suspended , which prevents subsequently created bridges from using it . < / para >
< / description >
2016-08-14 01:15:58 +00:00
< see - also >
< ref type = " manager " > BridgeTechnologySuspend < / ref >
< ref type = " manager " > BridgeTechnologyUnsuspend < / ref >
< / see - also >
2013-11-01 21:51:20 +00:00
< / manager >
< manager name = " BridgeTechnologyUnsuspend " language = " en_US " >
< synopsis >
Unsuspend a bridging technology .
< / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID']) " / >
< parameter name = " BridgeTechnology " required = " true " >
< para > The name of the bridging technology to unsuspend . < / para >
< / parameter >
< / syntax >
< description >
< para > Clears a previously suspended bridging technology , which allows subsequently created bridges to use it . < / para >
< / description >
2016-08-14 01:15:58 +00:00
< see - also >
< ref type = " manager " > BridgeTechnologyList < / ref >
< ref type = " manager " > BridgeTechnologySuspend < / ref >
< / see - also >
2013-11-01 21:51:20 +00:00
< / manager >
* * */
2009-03-05 18:18:27 +00:00
# include "asterisk.h"
# include "asterisk/logger.h"
# include "asterisk/channel.h"
# include "asterisk/options.h"
# include "asterisk/utils.h"
# include "asterisk/lock.h"
# include "asterisk/linkedlists.h"
2013-07-25 04:06:32 +00:00
# include "asterisk/bridge.h"
# include "asterisk/bridge_internal.h"
# include "asterisk/bridge_channel_internal.h"
2013-08-02 02:32:44 +00:00
# include "asterisk/bridge_features.h"
2013-07-25 04:06:32 +00:00
# include "asterisk/bridge_basic.h"
# include "asterisk/bridge_technology.h"
# include "asterisk/bridge_channel.h"
# include "asterisk/bridge_after.h"
# include "asterisk/stasis_bridges.h"
2013-05-31 15:34:20 +00:00
# include "asterisk/stasis_channels.h"
2013-08-01 13:49:34 +00:00
# include "asterisk/stasis_cache_pattern.h"
2009-03-05 18:18:27 +00:00
# include "asterisk/app.h"
# include "asterisk/file.h"
# include "asterisk/module.h"
# include "asterisk/astobj2.h"
2013-05-21 18:00:22 +00:00
# include "asterisk/pbx.h"
2011-12-22 20:44:53 +00:00
# include "asterisk/test.h"
2013-05-21 18:00:22 +00:00
# include "asterisk/_private.h"
# include "asterisk/heap.h"
# include "asterisk/say.h"
# include "asterisk/timing.h"
# include "asterisk/stringfields.h"
# include "asterisk/musiconhold.h"
# include "asterisk/features.h"
# include "asterisk/cli.h"
# include "asterisk/parking.h"
2013-05-28 14:45:31 +00:00
# include "asterisk/core_local.h"
2013-07-08 14:26:40 +00:00
# include "asterisk/core_unreal.h"
2013-08-22 21:09:52 +00:00
# include "asterisk/causes.h"
2013-05-21 18:00:22 +00:00
/*! All bridges container. */
static struct ao2_container * bridges ;
2009-03-05 18:18:27 +00:00
static AST_RWLIST_HEAD_STATIC ( bridge_technologies , ast_bridge_technology ) ;
2013-08-22 18:52:41 +00:00
static unsigned int optimization_id ;
2009-03-05 18:18:27 +00:00
/* Initial starting point for the bridge array of channels */
# define BRIDGE_ARRAY_START 128
/* Grow rate of bridge array of channels */
# define BRIDGE_ARRAY_GROW 32
2013-12-13 20:13:22 +00:00
/* Variable name - stores peer information about the most recent blind transfer */
# define BLINDTRANSFER "BLINDTRANSFER"
/* Variable name - stores peer information about the most recent attended transfer */
# define ATTENDEDTRANSFER "ATTENDEDTRANSFER"
2011-06-30 20:33:15 +00:00
static void cleanup_video_mode ( struct ast_bridge * bridge ) ;
2009-03-05 18:18:27 +00:00
/*! Default DTMF keys for built in features */
static char builtin_features_dtmf [ AST_BRIDGE_BUILTIN_END ] [ MAXIMUM_DTMF_FEATURE_STRING ] ;
/*! Function handlers for the built in features */
2013-07-24 14:35:03 +00:00
static ast_bridge_hook_callback builtin_features_handlers [ AST_BRIDGE_BUILTIN_END ] ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*! Function handlers for built in interval features */
static ast_bridge_builtin_set_limits_fn builtin_interval_handlers [ AST_BRIDGE_BUILTIN_INTERVAL_END ] ;
/*! Bridge manager service request */
struct bridge_manager_request {
/*! List of bridge service requests. */
AST_LIST_ENTRY ( bridge_manager_request ) node ;
/*! Refed bridge requesting service. */
struct ast_bridge * bridge ;
} ;
struct bridge_manager_controller {
/*! Condition, used to wake up the bridge manager thread. */
ast_cond_t cond ;
/*! Queue of bridge service requests. */
AST_LIST_HEAD_NOLOCK ( , bridge_manager_request ) service_requests ;
/*! Manager thread */
pthread_t thread ;
/*! TRUE if the manager needs to stop. */
unsigned int stop : 1 ;
} ;
/*! Bridge manager controller. */
static struct bridge_manager_controller * bridge_manager ;
/*!
* \ internal
* \ brief Request service for a bridge from the bridge manager .
* \ since 12.0 .0
*
* \ param bridge Requesting service .
*
* \ return Nothing
*/
static void bridge_manager_service_req ( struct ast_bridge * bridge )
{
struct bridge_manager_request * request ;
ao2_lock ( bridge_manager ) ;
if ( bridge_manager - > stop ) {
ao2_unlock ( bridge_manager ) ;
return ;
}
/* Create the service request. */
request = ast_calloc ( 1 , sizeof ( * request ) ) ;
if ( ! request ) {
/* Well. This isn't good. */
ao2_unlock ( bridge_manager ) ;
return ;
}
ao2_ref ( bridge , + 1 ) ;
request - > bridge = bridge ;
/* Put request into the queue and wake the bridge manager. */
AST_LIST_INSERT_TAIL ( & bridge_manager - > service_requests , request , node ) ;
ast_cond_signal ( & bridge_manager - > cond ) ;
ao2_unlock ( bridge_manager ) ;
}
2009-03-05 18:18:27 +00:00
int __ast_bridge_technology_register ( struct ast_bridge_technology * technology , struct ast_module * module )
{
2013-05-21 18:00:22 +00:00
struct ast_bridge_technology * current ;
2009-03-05 18:18:27 +00:00
/* Perform a sanity check to make sure the bridge technology conforms to our needed requirements */
2013-05-21 18:00:22 +00:00
if ( ast_strlen_zero ( technology - > name )
| | ! technology - > capabilities
| | ! technology - > write ) {
ast_log ( LOG_WARNING , " Bridge technology %s failed registration sanity check. \n " ,
technology - > name ) ;
2009-03-05 18:18:27 +00:00
return - 1 ;
}
AST_RWLIST_WRLOCK ( & bridge_technologies ) ;
/* Look for duplicate bridge technology already using this name, or already registered */
AST_RWLIST_TRAVERSE ( & bridge_technologies , current , entry ) {
if ( ( ! strcasecmp ( current - > name , technology - > name ) ) | | ( current = = technology ) ) {
2013-05-21 18:00:22 +00:00
ast_log ( LOG_WARNING , " A bridge technology of %s already claims to exist in our world. \n " ,
technology - > name ) ;
2009-03-05 18:18:27 +00:00
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
return - 1 ;
}
}
/* Copy module pointer so reference counting can keep the module from unloading */
technology - > mod = module ;
/* Insert our new bridge technology into the list and print out a pretty message */
AST_RWLIST_INSERT_TAIL ( & bridge_technologies , technology , entry ) ;
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
2011-11-29 18:43:16 +00:00
ast_verb ( 2 , " Registered bridge technology %s \n " , technology - > name ) ;
2009-03-05 18:18:27 +00:00
return 0 ;
}
int ast_bridge_technology_unregister ( struct ast_bridge_technology * technology )
{
2013-05-21 18:00:22 +00:00
struct ast_bridge_technology * current ;
2009-03-05 18:18:27 +00:00
AST_RWLIST_WRLOCK ( & bridge_technologies ) ;
/* Ensure the bridge technology is registered before removing it */
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & bridge_technologies , current , entry ) {
if ( current = = technology ) {
AST_RWLIST_REMOVE_CURRENT ( entry ) ;
2011-11-29 18:43:16 +00:00
ast_verb ( 2 , " Unregistered bridge technology %s \n " , technology - > name ) ;
2009-03-05 18:18:27 +00:00
break ;
}
}
2009-04-10 03:55:27 +00:00
AST_RWLIST_TRAVERSE_SAFE_END ;
2009-03-05 18:18:27 +00:00
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
return current ? 0 : - 1 ;
}
2013-01-21 20:24:23 +00:00
/*!
* \ internal
2013-05-21 18:00:22 +00:00
* \ brief Put an action onto the specified bridge . Don ' t dup the action frame .
2013-01-21 20:24:23 +00:00
* \ since 12.0 .0
*
2013-05-21 18:00:22 +00:00
* \ param bridge What to queue the action on .
* \ param action What to do .
2013-01-21 20:24:23 +00:00
*
* \ return Nothing
*/
2013-05-21 18:00:22 +00:00
static void bridge_queue_action_nodup ( struct ast_bridge * bridge , struct ast_frame * action )
2013-01-21 20:24:23 +00:00
{
2014-05-09 22:49:26 +00:00
ast_debug ( 1 , " Bridge %s: queueing action type:%u sub:%d \n " ,
2013-05-21 18:00:22 +00:00
bridge - > uniqueid , action - > frametype , action - > subclass . integer ) ;
2013-01-21 20:24:23 +00:00
2013-05-21 18:00:22 +00:00
ast_bridge_lock ( bridge ) ;
AST_LIST_INSERT_TAIL ( & bridge - > action_queue , action , frame_list ) ;
ast_bridge_unlock ( bridge ) ;
bridge_manager_service_req ( bridge ) ;
2013-01-21 20:24:23 +00:00
}
2013-05-21 18:00:22 +00:00
int ast_bridge_queue_action ( struct ast_bridge * bridge , struct ast_frame * action )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
struct ast_frame * dup ;
dup = ast_frdup ( action ) ;
if ( ! dup ) {
return - 1 ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
bridge_queue_action_nodup ( bridge , dup ) ;
return 0 ;
}
2009-03-05 18:18:27 +00:00
2013-08-22 21:09:52 +00:00
void bridge_dissolve ( struct ast_bridge * bridge , int cause )
2013-01-21 20:24:23 +00:00
{
struct ast_bridge_channel * bridge_channel ;
2013-05-21 18:00:22 +00:00
struct ast_frame action = {
. frametype = AST_FRAME_BRIDGE_ACTION ,
2013-07-24 19:24:09 +00:00
. subclass . integer = BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING ,
2013-05-21 18:00:22 +00:00
} ;
2013-01-21 20:24:23 +00:00
2013-05-21 18:00:22 +00:00
if ( bridge - > dissolved ) {
return ;
2013-01-21 20:24:23 +00:00
}
2013-05-21 18:00:22 +00:00
bridge - > dissolved = 1 ;
2013-01-21 20:24:23 +00:00
2013-08-22 21:09:52 +00:00
if ( cause < = 0 ) {
cause = AST_CAUSE_NORMAL_CLEARING ;
}
bridge - > cause = cause ;
ast_debug ( 1 , " Bridge %s: dissolving bridge with cause %d(%s) \n " ,
bridge - > uniqueid , cause , ast_cause2str ( cause ) ) ;
2013-05-21 18:00:22 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , cause ) ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/* Must defer dissolving bridge because it is already locked. */
ast_bridge_queue_action ( bridge , & action ) ;
2009-03-05 18:18:27 +00:00
}
2013-06-25 22:28:22 +00:00
/*!
* \ internal
2013-06-26 14:38:57 +00:00
* \ brief Check if a bridge should dissolve because of a stolen channel and do it .
2013-06-25 22:28:22 +00:00
* \ since 12.0 .0
*
2013-06-26 14:38:57 +00:00
* \ param bridge Bridge to check .
* \ param bridge_channel Stolen channel causing the check . It is not in the bridge to check and may be in another bridge .
2013-06-25 22:28:22 +00:00
*
2013-06-26 14:38:57 +00:00
* \ note On entry , bridge and bridge_channel - > bridge are already locked .
2013-06-25 22:28:22 +00:00
*
* \ return Nothing
*/
2013-06-26 14:38:57 +00:00
static void bridge_dissolve_check_stolen ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel )
2013-06-25 22:28:22 +00:00
{
2013-06-26 14:38:57 +00:00
if ( bridge - > dissolved ) {
return ;
}
2013-06-25 22:28:22 +00:00
2013-06-26 14:38:57 +00:00
if ( bridge_channel - > features - > usable
& & ast_test_flag ( & bridge_channel - > features - > feature_flags ,
AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP ) ) {
/* The stolen channel controlled the bridge it was stolen from. */
2013-08-22 21:09:52 +00:00
bridge_dissolve ( bridge , 0 ) ;
2013-06-26 14:38:57 +00:00
return ;
}
if ( bridge - > num_channels < 2
& & ast_test_flag ( & bridge - > feature_flags , AST_BRIDGE_FLAG_DISSOLVE_HANGUP ) ) {
/*
* The stolen channel has not left enough channels to keep the
* bridge alive . Assume the stolen channel hung up .
*/
2013-08-22 21:09:52 +00:00
bridge_dissolve ( bridge , 0 ) ;
2013-06-26 14:38:57 +00:00
return ;
2013-06-25 22:28:22 +00:00
}
2009-05-10 17:07:46 +00:00
}
2013-07-12 21:42:53 +00:00
/*!
* \ internal
* \ brief Update connected line information after a bridge has been reconfigured .
*
* \ param bridge The bridge itself .
*
* \ return Nothing
*/
static void bridge_reconfigured_connected_line_update ( struct ast_bridge * bridge )
{
struct ast_party_connected_line connected ;
struct ast_bridge_channel * bridge_channel = AST_LIST_FIRST ( & bridge - > channels ) , * peer ;
unsigned char data [ 1024 ] ;
size_t datalen ;
if ( ! bridge_channel | |
! ( bridge - > technology - > capabilities & ( AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE ) ) | |
! ( peer = ast_bridge_channel_peer ( bridge_channel ) ) | |
ast_test_flag ( ast_channel_flags ( bridge_channel - > chan ) , AST_FLAG_ZOMBIE ) | |
ast_test_flag ( ast_channel_flags ( peer - > chan ) , AST_FLAG_ZOMBIE ) | |
ast_check_hangup_locked ( bridge_channel - > chan ) | |
ast_check_hangup_locked ( peer - > chan ) ) {
return ;
}
ast_party_connected_line_init ( & connected ) ;
ast_channel_lock ( bridge_channel - > chan ) ;
ast_connected_line_copy_from_caller ( & connected , ast_channel_caller ( bridge_channel - > chan ) ) ;
ast_channel_unlock ( bridge_channel - > chan ) ;
if ( ( datalen = ast_connected_line_build_data ( data , sizeof ( data ) , & connected , NULL ) ) ! = ( size_t ) - 1 ) {
ast_bridge_channel_queue_control_data ( peer , AST_CONTROL_CONNECTED_LINE , data , datalen ) ;
}
ast_channel_lock ( peer - > chan ) ;
ast_connected_line_copy_from_caller ( & connected , ast_channel_caller ( peer - > chan ) ) ;
ast_channel_unlock ( peer - > chan ) ;
if ( ( datalen = ast_connected_line_build_data ( data , sizeof ( data ) , & connected , NULL ) ) ! = ( size_t ) - 1 ) {
ast_bridge_channel_queue_control_data ( bridge_channel , AST_CONTROL_CONNECTED_LINE , data , datalen ) ;
}
ast_party_connected_line_free ( & connected ) ;
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Complete joining a channel to the bridge .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge What to operate upon .
* \ param bridge_channel What is joining the bridge technology .
2013-05-21 18:00:22 +00:00
*
2013-07-24 15:38:18 +00:00
* \ note On entry , bridge is already locked .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void bridge_channel_complete_join ( struct ast_bridge * bridge , struct ast_bridge_channel * bridge_channel )
2011-04-21 18:11:40 +00:00
{
2013-07-24 15:38:18 +00:00
/* Tell the bridge technology we are joining so they set us up */
ast_debug ( 1 , " Bridge %s: %p(%s) is joining %s technology \n " ,
bridge - > uniqueid , bridge_channel , ast_channel_name ( bridge_channel - > chan ) ,
bridge - > technology - > name ) ;
if ( bridge - > technology - > join
& & bridge - > technology - > join ( bridge , bridge_channel ) ) {
2016-04-13 18:20:23 +00:00
/* We cannot leave the channel partially in the bridge so we must kick it out */
ast_debug ( 1 , " Bridge %s: %p(%s) failed to join %s technology (Kicking it out) \n " ,
2013-05-21 18:00:22 +00:00
bridge - > uniqueid , bridge_channel , ast_channel_name ( bridge_channel - > chan ) ,
bridge - > technology - > name ) ;
2013-07-24 15:38:18 +00:00
bridge_channel - > just_joined = 1 ;
2016-04-13 18:20:23 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel , BRIDGE_CHANNEL_STATE_END , 0 ) ;
2013-07-24 15:38:18 +00:00
return ;
2013-06-17 03:00:38 +00:00
}
2013-07-24 15:38:18 +00:00
bridge_channel - > just_joined = 0 ;
2017-04-25 16:49:16 +00:00
/*
* When a channel joins the bridge its streams need to be mapped to the bridge ' s
* media types vector . This way all streams map to the same media type index for
* a given channel .
*/
2017-08-06 16:15:34 +00:00
if ( bridge_channel - > bridge - > technology - > stream_topology_changed ) {
bridge_channel - > bridge - > technology - > stream_topology_changed (
bridge_channel - > bridge , bridge_channel ) ;
} else {
ast_bridge_channel_stream_map ( bridge_channel ) ;
}
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Complete joining new channels to the bridge .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge Check for new channels on this bridge .
2013-05-21 18:00:22 +00:00
*
2013-07-24 15:38:18 +00:00
* \ note On entry , bridge is already locked .
2013-05-21 18:00:22 +00:00
*
2013-07-24 15:38:18 +00:00
* \ return Nothing
2013-05-21 18:00:22 +00:00
*/
2013-07-24 15:38:18 +00:00
static void bridge_complete_join ( struct ast_bridge * bridge )
2009-03-05 18:18:27 +00:00
{
2013-07-24 15:38:18 +00:00
struct ast_bridge_channel * bridge_channel ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
if ( bridge - > dissolved ) {
/*
* No sense in completing the join on channels for a dissolved
* bridge . They are just going to be removed soon anyway .
* However , we do have reason to abort here because the bridge
* technology may not be able to handle the number of channels
* still in the bridge .
*/
return ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
2017-06-08 19:38:51 +00:00
bridge_channel_queue_deferred_frames ( bridge_channel ) ;
2013-07-24 15:38:18 +00:00
if ( ! bridge_channel - > just_joined ) {
continue ;
}
bridge_channel_complete_join ( bridge , bridge_channel ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
}
2013-07-08 14:26:40 +00:00
2013-07-24 15:38:18 +00:00
/*! \brief Helper function used to find the "best" bridge technology given specified capabilities */
static struct ast_bridge_technology * find_best_technology ( uint32_t capabilities , struct ast_bridge * bridge )
{
struct ast_bridge_technology * current ;
struct ast_bridge_technology * best = NULL ;
2013-07-08 14:26:40 +00:00
2013-07-24 15:38:18 +00:00
AST_RWLIST_RDLOCK ( & bridge_technologies ) ;
AST_RWLIST_TRAVERSE ( & bridge_technologies , current , entry ) {
if ( current - > suspended ) {
ast_debug ( 1 , " Bridge technology %s is suspended. Skipping. \n " ,
current - > name ) ;
continue ;
}
if ( ! ( current - > capabilities & capabilities ) ) {
ast_debug ( 1 , " Bridge technology %s does not have any capabilities we want. \n " ,
current - > name ) ;
continue ;
}
if ( best & & current - > preference < = best - > preference ) {
2014-05-09 22:49:26 +00:00
ast_debug ( 1 , " Bridge technology %s has less preference than %s (%u <= %u). Skipping. \n " ,
2013-07-24 15:38:18 +00:00
current - > name , best - > name , current - > preference , best - > preference ) ;
continue ;
}
if ( current - > compatible & & ! current - > compatible ( bridge ) ) {
ast_debug ( 1 , " Bridge technology %s is not compatible with properties of existing bridge. \n " ,
current - > name ) ;
continue ;
}
best = current ;
2009-03-05 18:18:27 +00:00
}
2013-07-24 15:38:18 +00:00
if ( best ) {
/* Increment it's module reference count if present so it does not get unloaded while in use */
ast_module_ref ( best - > mod ) ;
ast_debug ( 1 , " Chose bridge technology %s \n " , best - > name ) ;
}
2013-06-06 22:46:54 +00:00
2013-07-24 15:38:18 +00:00
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
return best ;
2009-03-05 18:18:27 +00:00
}
2013-07-24 15:38:18 +00:00
struct tech_deferred_destroy {
struct ast_bridge_technology * tech ;
void * tech_pvt ;
} ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief Deferred destruction of bridge tech private structure .
* \ since 12.0 .0
*
* \ param bridge What to execute the action on .
* \ param action Deferred bridge tech destruction .
*
* \ note On entry , bridge must not be locked .
*
* \ return Nothing
*/
static void bridge_tech_deferred_destroy ( struct ast_bridge * bridge , struct ast_frame * action )
{
struct tech_deferred_destroy * deferred = action - > data . ptr ;
struct ast_bridge dummy_bridge = {
. technology = deferred - > tech ,
. tech_pvt = deferred - > tech_pvt ,
2013-12-17 23:25:49 +00:00
. creator = bridge - > creator ,
. name = bridge - > name ,
. uniqueid = bridge - > uniqueid ,
2013-07-24 15:38:18 +00:00
} ;
2012-06-26 21:45:22 +00:00
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s: calling %s technology destructor (deferred, dummy) \n " ,
dummy_bridge . uniqueid , dummy_bridge . technology - > name ) ;
dummy_bridge . technology - > destroy ( & dummy_bridge ) ;
ast_module_unref ( dummy_bridge . technology - > mod ) ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Handle bridge action frame .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge What to execute the action on .
* \ param action What to do .
*
* \ note On entry , bridge is already locked .
* \ note Can be called by the bridge destructor .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void bridge_action_bridge ( struct ast_bridge * bridge , struct ast_frame * action )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
#if 0 /* In case we need to know when the destructor is calling us. */
int in_destructor = ! ao2_ref ( bridge , 0 ) ;
# endif
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
switch ( action - > subclass . integer ) {
2013-07-24 19:24:09 +00:00
case BRIDGE_CHANNEL_ACTION_DEFERRED_TECH_DESTROY :
2013-07-24 15:38:18 +00:00
ast_bridge_unlock ( bridge ) ;
bridge_tech_deferred_destroy ( bridge , action ) ;
ast_bridge_lock ( bridge ) ;
break ;
2013-07-24 19:24:09 +00:00
case BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING :
2013-07-24 15:38:18 +00:00
ast_bridge_unlock ( bridge ) ;
bridge - > v_table - > dissolving ( bridge ) ;
ast_bridge_lock ( bridge ) ;
break ;
default :
/* Unexpected deferred action type. Should never happen. */
ast_assert ( 0 ) ;
break ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief Do any pending bridge actions .
* \ since 12.0 .0
*
* \ param bridge What to do actions on .
*
* \ note On entry , bridge is already locked .
* \ note Can be called by the bridge destructor .
*
* \ return Nothing
*/
static void bridge_handle_actions ( struct ast_bridge * bridge )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
struct ast_frame * action ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
while ( ( action = AST_LIST_REMOVE_HEAD ( & bridge - > action_queue , frame_list ) ) ) {
switch ( action - > frametype ) {
case AST_FRAME_BRIDGE_ACTION :
bridge_action_bridge ( bridge , action ) ;
break ;
default :
/* Unexpected deferred frame type. Should never happen. */
ast_assert ( 0 ) ;
break ;
}
ast_frfree ( action ) ;
}
2009-03-05 18:18:27 +00:00
}
2013-07-24 15:38:18 +00:00
static struct stasis_message * create_bridge_snapshot_message ( struct ast_bridge * bridge )
2009-03-05 18:18:27 +00:00
{
2013-07-24 15:38:18 +00:00
RAII_VAR ( struct ast_bridge_snapshot * , snapshot , NULL , ao2_cleanup ) ;
2009-03-05 18:18:27 +00:00
2014-08-06 12:55:28 +00:00
if ( ! ast_bridge_snapshot_type ( ) ) {
return NULL ;
}
2014-04-11 12:43:34 +00:00
ast_bridge_lock ( bridge ) ;
2013-07-24 15:38:18 +00:00
snapshot = ast_bridge_snapshot_create ( bridge ) ;
2014-04-11 12:43:34 +00:00
ast_bridge_unlock ( bridge ) ;
2013-07-24 15:38:18 +00:00
if ( ! snapshot ) {
return NULL ;
}
return stasis_message_create ( ast_bridge_snapshot_type ( ) , snapshot ) ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
static void destroy_bridge ( void * obj )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
struct ast_bridge * bridge = obj ;
2013-06-21 17:48:14 +00:00
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s: actually destroying %s bridge, nobody wants it anymore \n " ,
bridge - > uniqueid , bridge - > v_table - > name ) ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
if ( bridge - > construction_completed ) {
RAII_VAR ( struct stasis_message * , clear_msg , NULL , ao2_cleanup ) ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
clear_msg = create_bridge_snapshot_message ( bridge ) ;
if ( clear_msg ) {
RAII_VAR ( struct stasis_message * , msg , NULL , ao2_cleanup ) ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
msg = stasis_cache_clear_create ( clear_msg ) ;
if ( msg ) {
stasis_publish ( ast_bridge_topic ( bridge ) , msg ) ;
}
}
}
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
/* Do any pending actions in the context of destruction. */
ast_bridge_lock ( bridge ) ;
bridge_handle_actions ( bridge ) ;
ast_bridge_unlock ( bridge ) ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
/* There should not be any channels left in the bridge. */
ast_assert ( AST_LIST_EMPTY ( & bridge - > channels ) ) ;
2013-05-31 15:34:20 +00:00
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s: calling %s bridge destructor \n " ,
bridge - > uniqueid , bridge - > v_table - > name ) ;
bridge - > v_table - > destroy ( bridge ) ;
2013-05-31 15:34:20 +00:00
2013-07-24 15:38:18 +00:00
/* Pass off the bridge to the technology to destroy if needed */
if ( bridge - > technology ) {
ast_debug ( 1 , " Bridge %s: calling %s technology stop \n " ,
bridge - > uniqueid , bridge - > technology - > name ) ;
if ( bridge - > technology - > stop ) {
ast_bridge_lock ( bridge ) ;
bridge - > technology - > stop ( bridge ) ;
ast_bridge_unlock ( bridge ) ;
}
ast_debug ( 1 , " Bridge %s: calling %s technology destructor \n " ,
bridge - > uniqueid , bridge - > technology - > name ) ;
if ( bridge - > technology - > destroy ) {
bridge - > technology - > destroy ( bridge ) ;
}
ast_module_unref ( bridge - > technology - > mod ) ;
bridge - > technology = NULL ;
2013-05-31 15:34:20 +00:00
}
2017-04-25 16:49:16 +00:00
AST_VECTOR_FREE ( & bridge - > media_types ) ;
2015-03-13 01:12:35 +00:00
bridge - > callid = 0 ;
2013-07-24 15:38:18 +00:00
cleanup_video_mode ( bridge ) ;
2013-08-01 13:49:34 +00:00
stasis_cp_single_unsubscribe ( bridge - > topics ) ;
2013-12-17 23:25:49 +00:00
ast_string_field_free_memory ( bridge ) ;
2013-05-31 15:34:20 +00:00
}
2013-07-24 15:38:18 +00:00
struct ast_bridge * bridge_register ( struct ast_bridge * bridge )
2013-05-31 15:34:20 +00:00
{
2013-07-24 15:38:18 +00:00
if ( bridge ) {
bridge - > construction_completed = 1 ;
2014-04-11 12:43:34 +00:00
ast_bridge_lock ( bridge ) ;
2013-07-24 15:38:18 +00:00
ast_bridge_publish_state ( bridge ) ;
2014-04-11 12:43:34 +00:00
ast_bridge_unlock ( bridge ) ;
2013-07-24 15:38:18 +00:00
if ( ! ao2_link ( bridges , bridge ) ) {
2013-08-22 21:09:52 +00:00
ast_bridge_destroy ( bridge , 0 ) ;
2013-07-24 15:38:18 +00:00
bridge = NULL ;
}
}
return bridge ;
2013-05-31 15:34:20 +00:00
}
2013-07-24 15:38:18 +00:00
struct ast_bridge * bridge_alloc ( size_t size , const struct ast_bridge_methods * v_table )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
struct ast_bridge * bridge ;
2013-01-21 18:45:17 +00:00
2013-07-24 15:38:18 +00:00
/* Check v_table that all methods are present. */
if ( ! v_table
| | ! v_table - > name
| | ! v_table - > destroy
| | ! v_table - > dissolving
| | ! v_table - > push
| | ! v_table - > pull
| | ! v_table - > notify_masquerade
| | ! v_table - > get_merge_priority ) {
ast_log ( LOG_ERROR , " Virtual method table for bridge class %s not complete. \n " ,
v_table & & v_table - > name ? v_table - > name : " <unknown> " ) ;
ast_assert ( 0 ) ;
return NULL ;
}
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
bridge = ao2_alloc ( size , destroy_bridge ) ;
2013-12-17 23:25:49 +00:00
if ( ! bridge ) {
return NULL ;
}
if ( ast_string_field_init ( bridge , 80 ) ) {
ao2_cleanup ( bridge ) ;
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-12-17 23:25:49 +00:00
bridge - > v_table = v_table ;
2017-04-25 16:49:16 +00:00
AST_VECTOR_INIT ( & bridge - > media_types , AST_MEDIA_TYPE_END ) ;
2013-07-24 15:38:18 +00:00
return bridge ;
2009-03-05 18:18:27 +00:00
}
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
struct ast_bridge * bridge_base_init ( struct ast_bridge * self , uint32_t capabilities , unsigned int flags , const char * creator , const char * name , const char * id )
2009-03-05 18:18:27 +00:00
{
2013-12-17 23:25:49 +00:00
char uuid_hold [ AST_UUID_STR_LEN ] ;
2013-07-24 15:38:18 +00:00
if ( ! self ) {
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-07-24 15:38:18 +00:00
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
if ( ! ast_strlen_zero ( id ) ) {
ast_string_field_set ( self , uniqueid , id ) ;
} else {
ast_uuid_generate_str ( uuid_hold , AST_UUID_STR_LEN ) ;
ast_string_field_set ( self , uniqueid , uuid_hold ) ;
}
2013-12-17 23:25:49 +00:00
ast_string_field_set ( self , creator , creator ) ;
if ( ! ast_strlen_zero ( creator ) ) {
ast_string_field_set ( self , name , name ) ;
}
2013-07-24 15:38:18 +00:00
ast_set_flag ( & self - > feature_flags , flags ) ;
self - > allowed_capabilities = capabilities ;
2016-05-09 19:27:53 +00:00
if ( ! ( flags & AST_BRIDGE_FLAG_INVISIBLE ) ) {
if ( bridge_topics_init ( self ) ! = 0 ) {
ast_log ( LOG_WARNING , " Bridge %s: Could not initialize topics \n " ,
self - > uniqueid ) ;
ao2_ref ( self , - 1 ) ;
return NULL ;
}
2013-08-01 13:49:34 +00:00
}
2013-07-24 15:38:18 +00:00
/* Use our helper function to find the "best" bridge technology. */
self - > technology = find_best_technology ( capabilities , self ) ;
if ( ! self - > technology ) {
ast_log ( LOG_WARNING , " Bridge %s: Could not create class %s. No technology to support it. \n " ,
self - > uniqueid , self - > v_table - > name ) ;
ao2_ref ( self , - 1 ) ;
return NULL ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
/* Pass off the bridge to the technology to manipulate if needed */
ast_debug ( 1 , " Bridge %s: calling %s technology constructor \n " ,
self - > uniqueid , self - > technology - > name ) ;
if ( self - > technology - > create & & self - > technology - > create ( self ) ) {
ast_log ( LOG_WARNING , " Bridge %s: failed to setup bridge technology %s \n " ,
self - > uniqueid , self - > technology - > name ) ;
ao2_ref ( self , - 1 ) ;
return NULL ;
}
ast_debug ( 1 , " Bridge %s: calling %s technology start \n " ,
self - > uniqueid , self - > technology - > name ) ;
if ( self - > technology - > start & & self - > technology - > start ( self ) ) {
ast_log ( LOG_WARNING , " Bridge %s: failed to start bridge technology %s \n " ,
self - > uniqueid , self - > technology - > name ) ;
ao2_ref ( self , - 1 ) ;
return NULL ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2016-05-09 19:27:53 +00:00
if ( ! ( flags & AST_BRIDGE_FLAG_INVISIBLE ) ) {
if ( ! ast_bridge_topic ( self ) ) {
ao2_ref ( self , - 1 ) ;
return NULL ;
}
2013-07-24 15:38:18 +00:00
}
return self ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief ast_bridge base class destructor .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param self Bridge to operate upon .
*
* \ note Stub because of nothing to do .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void bridge_base_destroy ( struct ast_bridge * self )
2013-05-21 18:00:22 +00:00
{
}
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief The bridge is being dissolved .
* \ since 12.0 .0
*
* \ param self Bridge to operate upon .
*
* \ return Nothing
*/
static void bridge_base_dissolving ( struct ast_bridge * self )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
ao2_unlink ( bridges , self ) ;
}
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief ast_bridge base push method .
* \ since 12.0 .0
*
* \ param self Bridge to operate upon .
* \ param bridge_channel Bridge channel to push .
* \ param swap Bridge channel to swap places with if not NULL .
*
* \ note On entry , self is already locked .
* \ note Stub because of nothing to do .
*
* \ retval 0 on success
* \ retval - 1 on failure
*/
static int bridge_base_push ( struct ast_bridge * self , struct ast_bridge_channel * bridge_channel , struct ast_bridge_channel * swap )
2009-03-05 18:18:27 +00:00
{
2013-07-24 15:38:18 +00:00
return 0 ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief ast_bridge base pull method .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param self Bridge to operate upon .
* \ param bridge_channel Bridge channel to pull .
*
* \ note On entry , self is already locked .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void bridge_base_pull ( struct ast_bridge * self , struct ast_bridge_channel * bridge_channel )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
ast_bridge_features_remove ( bridge_channel - > features , AST_BRIDGE_HOOK_REMOVE_ON_PULL ) ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-06-06 22:46:54 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief ast_bridge base notify_masquerade method .
2013-06-06 22:46:54 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param self Bridge to operate upon .
* \ param bridge_channel Bridge channel that was masqueraded .
*
* \ note On entry , self is already locked .
2013-06-06 22:46:54 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void bridge_base_notify_masquerade ( struct ast_bridge * self , struct ast_bridge_channel * bridge_channel )
2013-06-06 22:46:54 +00:00
{
2013-07-24 15:38:18 +00:00
self - > reconfigured = 1 ;
2013-06-06 22:46:54 +00:00
}
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief Get the merge priority of this bridge .
* \ since 12.0 .0
*
* \ param self Bridge to operate upon .
*
* \ note On entry , self is already locked .
*
* \ return Merge priority
*/
static int bridge_base_get_merge_priority ( struct ast_bridge * self )
2013-06-06 22:46:54 +00:00
{
2013-07-24 15:38:18 +00:00
return 0 ;
2013-06-06 22:46:54 +00:00
}
2015-01-29 23:03:14 +00:00
/*!
* \ internal
* \ brief ast_bridge base push_peek method .
* \ since 13.2 .0
*
* \ param self Bridge to operate upon .
* \ param bridge_channel Bridge channel to push .
* \ param swap Bridge channel to swap places with if not NULL .
*
* \ note On entry , self is already locked .
* \ note Stub because of nothing to do .
*
* \ retval 0 on success
* \ retval - 1 on failure
*/
static int bridge_base_push_peek ( struct ast_bridge * self , struct ast_bridge_channel * bridge_channel , struct ast_bridge_channel * swap )
{
return 0 ;
}
2013-07-24 15:38:18 +00:00
struct ast_bridge_methods ast_bridge_base_v_table = {
. name = " base " ,
. destroy = bridge_base_destroy ,
. dissolving = bridge_base_dissolving ,
. push = bridge_base_push ,
. pull = bridge_base_pull ,
. notify_masquerade = bridge_base_notify_masquerade ,
. get_merge_priority = bridge_base_get_merge_priority ,
2015-01-29 23:03:14 +00:00
. push_peek = bridge_base_push_peek ,
2013-05-21 18:00:22 +00:00
} ;
2009-03-05 18:18:27 +00:00
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
struct ast_bridge * ast_bridge_base_new ( uint32_t capabilities , unsigned int flags , const char * creator , const char * name , const char * id )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
void * bridge ;
bridge = bridge_alloc ( sizeof ( struct ast_bridge ) , & ast_bridge_base_v_table ) ;
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
bridge = bridge_base_init ( bridge , capabilities , flags , creator , name , id ) ;
2013-07-24 15:38:18 +00:00
bridge = bridge_register ( bridge ) ;
return bridge ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-08-22 21:09:52 +00:00
int ast_bridge_destroy ( struct ast_bridge * bridge , int cause )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s: telling all channels to leave the party \n " , bridge - > uniqueid ) ;
ast_bridge_lock ( bridge ) ;
2013-08-22 21:09:52 +00:00
bridge_dissolve ( bridge , cause ) ;
2013-07-24 15:38:18 +00:00
ast_bridge_unlock ( bridge ) ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
ao2_ref ( bridge , - 1 ) ;
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
return 0 ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Perform the smart bridge operation .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge Work on this bridge .
*
* \ details
* Basically see if a new bridge technology should be used instead
* of the current one .
2013-05-21 18:00:22 +00:00
*
* \ note On entry , bridge is already locked .
*
2013-07-24 15:38:18 +00:00
* \ retval 0 on success .
* \ retval - 1 on error .
2013-05-21 18:00:22 +00:00
*/
2013-07-24 15:38:18 +00:00
static int smart_bridge_operation ( struct ast_bridge * bridge )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
uint32_t new_capabilities ;
struct ast_bridge_technology * new_technology ;
struct ast_bridge_technology * old_technology = bridge - > technology ;
2013-05-21 18:00:22 +00:00
struct ast_bridge_channel * bridge_channel ;
2013-07-24 15:38:18 +00:00
struct ast_frame * deferred_action ;
struct ast_bridge dummy_bridge = {
. technology = bridge - > technology ,
. tech_pvt = bridge - > tech_pvt ,
2013-12-17 23:25:49 +00:00
. creator = bridge - > creator ,
. name = bridge - > name ,
. uniqueid = bridge - > uniqueid ,
2013-07-24 15:38:18 +00:00
} ;
2013-05-21 18:00:22 +00:00
if ( bridge - > dissolved ) {
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s is dissolved, not performing smart bridge operation. \n " ,
bridge - > uniqueid ) ;
return 0 ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-07-24 15:38:18 +00:00
/* Determine new bridge technology capabilities needed. */
if ( 2 < bridge - > num_channels ) {
new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX ;
new_capabilities & = bridge - > allowed_capabilities ;
} else {
new_capabilities = AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX ;
new_capabilities & = bridge - > allowed_capabilities ;
if ( ! new_capabilities
& & ( bridge - > allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX ) ) {
/* Allow switching between different multimix bridge technologies. */
new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX ;
}
}
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
/* Find a bridge technology to satisfy the new capabilities. */
new_technology = find_best_technology ( new_capabilities , bridge ) ;
if ( ! new_technology ) {
int is_compatible = 0 ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
if ( old_technology - > compatible ) {
is_compatible = old_technology - > compatible ( bridge ) ;
} else if ( old_technology - > capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX ) {
is_compatible = 1 ;
} else if ( bridge - > num_channels < = 2
& & ( old_technology - > capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX ) ) {
is_compatible = 1 ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
if ( is_compatible ) {
ast_debug ( 1 , " Bridge %s could not get a new technology, staying with old technology. \n " ,
bridge - > uniqueid ) ;
return 0 ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
ast_log ( LOG_WARNING , " Bridge %s has no technology available to support it. \n " ,
bridge - > uniqueid ) ;
return - 1 ;
}
if ( new_technology = = old_technology ) {
ast_debug ( 1 , " Bridge %s is already using the new technology. \n " ,
bridge - > uniqueid ) ;
ast_module_unref ( old_technology - > mod ) ;
return 0 ;
}
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
if ( old_technology - > destroy ) {
struct tech_deferred_destroy deferred_tech_destroy = {
. tech = dummy_bridge . technology ,
. tech_pvt = dummy_bridge . tech_pvt ,
} ;
struct ast_frame action = {
. frametype = AST_FRAME_BRIDGE_ACTION ,
2013-07-24 19:24:09 +00:00
. subclass . integer = BRIDGE_CHANNEL_ACTION_DEFERRED_TECH_DESTROY ,
2013-07-24 15:38:18 +00:00
. data . ptr = & deferred_tech_destroy ,
. datalen = sizeof ( deferred_tech_destroy ) ,
} ;
2013-05-21 18:00:22 +00:00
/*
2013-07-24 15:38:18 +00:00
* We need to defer the bridge technology destroy callback
* because we have the bridge locked .
2013-05-21 18:00:22 +00:00
*/
2013-07-24 15:38:18 +00:00
deferred_action = ast_frdup ( & action ) ;
if ( ! deferred_action ) {
ast_module_unref ( new_technology - > mod ) ;
return - 1 ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
} else {
deferred_action = NULL ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
/*
* We are now committed to changing the bridge technology . We
* must not release the bridge lock until we have installed the
* new bridge technology .
*/
ast_verb ( 4 , " Bridge %s: switching from %s technology to %s \n " ,
bridge - > uniqueid , old_technology - > name , new_technology - > name ) ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
/*
* Since we are soon going to pass this bridge to a new
* technology we need to NULL out the tech_pvt pointer but
* don ' t worry as it still exists in dummy_bridge , ditto for the
* old technology .
*/
bridge - > tech_pvt = NULL ;
bridge - > technology = new_technology ;
2013-06-06 21:40:35 +00:00
2013-07-24 15:38:18 +00:00
/* Setup the new bridge technology. */
ast_debug ( 1 , " Bridge %s: calling %s technology constructor \n " ,
bridge - > uniqueid , new_technology - > name ) ;
if ( new_technology - > create & & new_technology - > create ( bridge ) ) {
ast_log ( LOG_WARNING , " Bridge %s: failed to setup bridge technology %s \n " ,
bridge - > uniqueid , new_technology - > name ) ;
bridge - > tech_pvt = dummy_bridge . tech_pvt ;
bridge - > technology = dummy_bridge . technology ;
ast_module_unref ( new_technology - > mod ) ;
return - 1 ;
2013-06-06 21:40:35 +00:00
}
2013-05-21 18:00:22 +00:00
2014-10-10 20:48:46 +00:00
/* To ensure that things are sane for the old technology move the channels it
* expects to the dummy bridge
*/
AST_LIST_TRAVERSE_SAFE_BEGIN ( & bridge - > channels , bridge_channel , entry ) {
if ( bridge_channel - > just_joined ) {
continue ;
}
ast_debug ( 1 , " Bridge %s: moving %p(%s) to dummy bridge temporarily \n " ,
bridge - > uniqueid , bridge_channel , ast_channel_name ( bridge_channel - > chan ) ) ;
AST_LIST_REMOVE_CURRENT ( entry ) ;
AST_LIST_INSERT_TAIL ( & dummy_bridge . channels , bridge_channel , entry ) ;
dummy_bridge . num_channels + + ;
if ( ast_test_flag ( & bridge_channel - > features - > feature_flags , AST_BRIDGE_CHANNEL_FLAG_LONELY ) ) {
dummy_bridge . num_lonely + + ;
}
if ( ! bridge_channel - > suspended ) {
dummy_bridge . num_active + + ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
/* Take all the channels out of the old technology */
AST_LIST_TRAVERSE_SAFE_BEGIN ( & dummy_bridge . channels , bridge_channel , entry ) {
ast_debug ( 1 , " Bridge %s: %p(%s) is leaving %s technology (dummy) \n " ,
dummy_bridge . uniqueid , bridge_channel , ast_channel_name ( bridge_channel - > chan ) ,
old_technology - > name ) ;
if ( old_technology - > leave ) {
old_technology - > leave ( & dummy_bridge , bridge_channel ) ;
}
AST_LIST_REMOVE_CURRENT ( entry ) ;
AST_LIST_INSERT_TAIL ( & bridge - > channels , bridge_channel , entry ) ;
dummy_bridge . num_channels - - ;
if ( ast_test_flag ( & bridge_channel - > features - > feature_flags , AST_BRIDGE_CHANNEL_FLAG_LONELY ) ) {
dummy_bridge . num_lonely - - ;
}
if ( ! bridge_channel - > suspended ) {
dummy_bridge . num_active - - ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s: calling %s technology stop \n " ,
dummy_bridge . uniqueid , old_technology - > name ) ;
if ( old_technology - > stop ) {
old_technology - > stop ( & dummy_bridge ) ;
}
2013-05-21 18:00:22 +00:00
2014-10-10 20:48:46 +00:00
/* Add any new channels or re-add existing channels to the bridge. */
2013-07-24 15:38:18 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
bridge_channel_complete_join ( bridge , bridge_channel ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
ast_debug ( 1 , " Bridge %s: calling %s technology start \n " ,
bridge - > uniqueid , new_technology - > name ) ;
if ( new_technology - > start & & new_technology - > start ( bridge ) ) {
ast_log ( LOG_WARNING , " Bridge %s: failed to start bridge technology %s \n " ,
bridge - > uniqueid , new_technology - > name ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
/*
* Now that all the channels have been moved over we need to get
* rid of all the information the old technology may have left
* around .
*/
if ( old_technology - > destroy ) {
ast_debug ( 1 , " Bridge %s: deferring %s technology destructor \n " ,
dummy_bridge . uniqueid , old_technology - > name ) ;
bridge_queue_action_nodup ( bridge , deferred_action ) ;
} else {
ast_debug ( 1 , " Bridge %s: calling %s technology destructor \n " ,
dummy_bridge . uniqueid , old_technology - > name ) ;
ast_module_unref ( old_technology - > mod ) ;
}
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
return 0 ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played .
* \ since 12.0 .0
*
* \ param bridge_channel What to check .
*
* \ return Nothing
*/
static void check_bridge_play_sound ( struct ast_bridge_channel * bridge_channel )
2013-05-28 14:45:31 +00:00
{
2013-07-24 15:38:18 +00:00
const char * play_file ;
2013-06-06 00:16:23 +00:00
2013-07-24 15:38:18 +00:00
ast_channel_lock ( bridge_channel - > chan ) ;
play_file = pbx_builtin_getvar_helper ( bridge_channel - > chan , " BRIDGE_PLAY_SOUND " ) ;
if ( ! ast_strlen_zero ( play_file ) ) {
play_file = ast_strdupa ( play_file ) ;
pbx_builtin_setvar_helper ( bridge_channel - > chan , " BRIDGE_PLAY_SOUND " , NULL ) ;
} else {
play_file = NULL ;
2013-06-06 00:16:23 +00:00
}
2013-07-24 15:38:18 +00:00
ast_channel_unlock ( bridge_channel - > chan ) ;
2013-07-12 21:42:53 +00:00
2013-07-24 15:38:18 +00:00
if ( play_file ) {
ast_bridge_channel_queue_playfile ( bridge_channel , NULL , play_file , NULL ) ;
2013-07-12 21:42:53 +00:00
}
2013-05-28 14:45:31 +00:00
}
2013-07-24 15:38:18 +00:00
/*!
* \ internal
* \ brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge .
* \ since 12.0 .0
*
* \ param bridge What to operate on .
*
* \ note On entry , the bridge is already locked .
*
* \ return Nothing
*/
static void check_bridge_play_sounds ( struct ast_bridge * bridge )
2013-05-28 14:45:31 +00:00
{
2013-07-24 15:38:18 +00:00
struct ast_bridge_channel * bridge_channel ;
2013-05-28 14:45:31 +00:00
2013-07-24 15:38:18 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
check_bridge_play_sound ( bridge_channel ) ;
}
2013-05-28 14:45:31 +00:00
}
2015-12-01 22:11:07 +00:00
void ast_bridge_vars_set ( struct ast_channel * chan , const char * name , const char * pvtid )
2013-05-28 14:45:31 +00:00
{
2013-10-02 16:23:34 +00:00
ast_channel_stage_snapshot ( chan ) ;
2013-07-24 15:38:18 +00:00
pbx_builtin_setvar_helper ( chan , " BRIDGEPEER " , name ) ;
pbx_builtin_setvar_helper ( chan , " BRIDGEPVTCALLID " , pvtid ) ;
2013-10-02 16:23:34 +00:00
ast_channel_stage_snapshot_done ( chan ) ;
2013-05-28 14:45:31 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param c0 Party of the first part .
* \ param c1 Party of the second part .
*
* \ note On entry , the bridge is already locked .
* \ note The bridge is expected to have exactly two parties .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void set_bridge_peer_vars_2party ( struct ast_channel * c0 , struct ast_channel * c1 )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
const char * c0_name ;
const char * c1_name ;
const char * c0_pvtid = NULL ;
const char * c1_pvtid = NULL ;
# define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \
do { \
name = ast_strdupa ( ast_channel_name ( chan ) ) ; \
if ( ast_channel_tech ( chan ) - > get_pvt_uniqueid ) { \
pvtid = ast_strdupa ( ast_channel_tech ( chan ) - > get_pvt_uniqueid ( chan ) ) ; \
} \
} while ( 0 )
ast_channel_lock ( c1 ) ;
UPDATE_BRIDGE_VARS_GET ( c1 , c1_name , c1_pvtid ) ;
ast_channel_unlock ( c1 ) ;
ast_channel_lock ( c0 ) ;
2015-12-01 22:11:07 +00:00
ast_bridge_vars_set ( c0 , c1_name , c1_pvtid ) ;
2013-07-24 15:38:18 +00:00
UPDATE_BRIDGE_VARS_GET ( c0 , c0_name , c0_pvtid ) ;
ast_channel_unlock ( c0 ) ;
ast_channel_lock ( c1 ) ;
2015-12-01 22:11:07 +00:00
ast_bridge_vars_set ( c1 , c0_name , c0_pvtid ) ;
2013-07-24 15:38:18 +00:00
ast_channel_unlock ( c1 ) ;
2013-05-21 18:00:22 +00:00
}
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param buf Buffer to fill . The caller must guarantee the buffer is large enough .
* \ param cur_idx Which index into names [ ] to skip .
* \ param names Channel names to put in the buffer .
* \ param num_names Number of names in the array .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void fill_bridgepeer_buf ( char * buf , unsigned int cur_idx , const char * names [ ] , unsigned int num_names )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
int need_separator = 0 ;
unsigned int idx ;
const char * src ;
char * pos ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
pos = buf ;
for ( idx = 0 ; idx < num_names ; + + idx ) {
if ( idx = = cur_idx ) {
continue ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
if ( need_separator ) {
* pos + + = ' , ' ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
need_separator = 1 ;
/* Copy name into buffer. */
src = names [ idx ] ;
while ( * src ) {
* pos + + = * src + + ;
2013-05-21 18:00:22 +00:00
}
}
2013-07-24 15:38:18 +00:00
* pos = ' \0 ' ;
2013-05-21 18:00:22 +00:00
}
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi - party bridge .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge What to operate on .
*
* \ note On entry , the bridge is already locked .
* \ note The bridge is expected to have more than two parties .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void set_bridge_peer_vars_multiparty ( struct ast_bridge * bridge )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
/*
* Set a maximum number of channel names for the BRIDGEPEER
* list . The plus one is for the current channel which is not
* put in the list .
*/
# define MAX_BRIDGEPEER_CHANS (10 + 1)
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
unsigned int idx ;
unsigned int num_names ;
unsigned int len ;
const char * * names ;
char * buf ;
struct ast_bridge_channel * bridge_channel ;
/* Get first MAX_BRIDGEPEER_CHANS channel names. */
num_names = MIN ( bridge - > num_channels , MAX_BRIDGEPEER_CHANS ) ;
names = ast_alloca ( num_names * sizeof ( * names ) ) ;
idx = 0 ;
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
if ( num_names < = idx ) {
break ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
ast_channel_lock ( bridge_channel - > chan ) ;
names [ idx + + ] = ast_strdupa ( ast_channel_name ( bridge_channel - > chan ) ) ;
ast_channel_unlock ( bridge_channel - > chan ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
/* Determine maximum buf size needed. */
len = num_names ;
for ( idx = 0 ; idx < num_names ; + + idx ) {
len + = strlen ( names [ idx ] ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
buf = ast_alloca ( len ) ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
/* Set the bridge channel variables. */
idx = 0 ;
buf [ 0 ] = ' \0 ' ;
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
if ( idx < num_names ) {
fill_bridgepeer_buf ( buf , idx , names , num_names ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
+ + idx ;
ast_channel_lock ( bridge_channel - > chan ) ;
2015-12-01 22:11:07 +00:00
ast_bridge_vars_set ( bridge_channel - > chan , buf , NULL ) ;
2013-07-24 15:38:18 +00:00
ast_channel_unlock ( bridge_channel - > chan ) ;
2013-05-21 18:00:22 +00:00
}
}
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge What to operate on .
2013-05-21 18:00:22 +00:00
*
2013-07-24 15:38:18 +00:00
* \ note On entry , the bridge is already locked .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void set_bridge_peer_vars_holding ( struct ast_bridge * bridge )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
struct ast_bridge_channel * bridge_channel ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
ast_channel_lock ( bridge_channel - > chan ) ;
2015-12-01 22:11:07 +00:00
ast_bridge_vars_set ( bridge_channel - > chan , NULL , NULL ) ;
2013-07-24 15:38:18 +00:00
ast_channel_unlock ( bridge_channel - > chan ) ;
2013-05-21 18:00:22 +00:00
}
}
/*!
* \ internal
2013-07-24 15:38:18 +00:00
* \ brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
2013-07-24 15:38:18 +00:00
* \ param bridge What to operate on .
*
* \ note On entry , the bridge is already locked .
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
static void set_bridge_peer_vars ( struct ast_bridge * bridge )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
if ( bridge - > technology - > capabilities & AST_BRIDGE_CAPABILITY_HOLDING ) {
set_bridge_peer_vars_holding ( bridge ) ;
return ;
}
if ( bridge - > num_channels < 2 ) {
return ;
}
if ( bridge - > num_channels = = 2 ) {
set_bridge_peer_vars_2party ( AST_LIST_FIRST ( & bridge - > channels ) - > chan ,
AST_LIST_LAST ( & bridge - > channels ) - > chan ) ;
} else {
set_bridge_peer_vars_multiparty ( bridge ) ;
2013-05-21 18:00:22 +00:00
}
}
2013-07-24 15:38:18 +00:00
void bridge_reconfigured ( struct ast_bridge * bridge , unsigned int colp_update )
2013-05-21 18:00:22 +00:00
{
2013-07-24 15:38:18 +00:00
if ( ! bridge - > reconfigured ) {
return ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
bridge - > reconfigured = 0 ;
if ( ast_test_flag ( & bridge - > feature_flags , AST_BRIDGE_FLAG_SMART )
& & smart_bridge_operation ( bridge ) ) {
/* Smart bridge failed. */
2013-08-22 21:09:52 +00:00
bridge_dissolve ( bridge , 0 ) ;
2013-07-24 15:38:18 +00:00
return ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
bridge_complete_join ( bridge ) ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
if ( bridge - > dissolved ) {
return ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 15:38:18 +00:00
check_bridge_play_sounds ( bridge ) ;
set_bridge_peer_vars ( bridge ) ;
ast_bridge_publish_state ( bridge ) ;
2013-05-21 18:00:22 +00:00
2013-07-24 15:38:18 +00:00
if ( colp_update ) {
bridge_reconfigured_connected_line_update ( bridge ) ;
2013-05-21 18:00:22 +00:00
}
}
2013-07-25 02:20:23 +00:00
struct ast_bridge_channel * bridge_find_channel ( struct ast_bridge * bridge , struct ast_channel * chan )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_channel * bridge_channel ;
2013-07-25 02:20:23 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
if ( bridge_channel - > chan = = chan ) {
Replace chan_agent with app_agent_pool.
The ill conceived chan_agent is no more. It is now replaced by
app_agent_pool.
Agents login using the AgentLogin() application as before. The
AgentLogin() application no longer does any authentication.
Authentication is now the responsibility of the dialplan. (Besides, the
authentication done by chan_agent did not match what the voice prompts
asked for.)
Sample extensions.conf
[login]
; Sample agent 1001 login
; Set COLP for in between calls so the agent does not see the last caller COLP.
exten => 1001,1,Set(CONNECTEDLINE(all)="Agent Waiting" <1001>)
; Give the agent DTMF transfer and disconnect features when connected to a caller.
same => n,Set(CHANNEL(dtmf-features)=TX)
same => n,AgentLogin(1001)
same => n,NoOp(AGENT_STATUS is ${AGENT_STATUS})
same => n,Hangup()
[caller]
; Sample caller direct connect to agent 1001
exten => 800,1,AgentRequest(1001)
same => n,NoOp(AGENT_STATUS is ${AGENT_STATUS})
same => n,Hangup()
; Sample caller going through a Queue to agent 1001
exten => 900,1,Queue(agent_q)
same => n,Hangup()
Sample queues.conf
[agent_q]
member => Local/800@caller,,SuperAgent,Agent:1001
Under the hood operation overview:
1) Logged in agents wait for callers in an agents holding bridge.
2) Caller requests an agent using AgentRequest()
3) A basic bridge is created, the agent is notified, and caller joins the
basic bridge to wait for the agent.
4) The agent is either automatically connected to the caller or must ack
the call to connect.
5) The agent is moved from the agents holding bridge to the basic bridge.
6) The agent and caller talk.
7) The connection is ended by either party.
8) The agent goes back to the agents holding bridge.
To avoid some locking issues with the agent holding bridge, I needed to
make some changes to the after bridge callback support. The after bridge
callback is now a list of requested callbacks with the last to be added
the only active callback. The after bridge callback for failed callbacks
will always happen in the channel thread when the channel leaves the
bridging system or is destroyed.
(closes issue ASTERISK-21554)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2657/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394417 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-07-15 23:20:55 +00:00
break ;
}
2013-05-21 18:00:22 +00:00
}
2013-07-25 02:20:23 +00:00
return bridge_channel ;
2013-05-21 18:00:22 +00:00
}
void ast_bridge_notify_masquerade ( struct ast_channel * chan )
{
struct ast_bridge_channel * bridge_channel ;
struct ast_bridge * bridge ;
/* Safely get the bridge_channel pointer for the chan. */
ast_channel_lock ( chan ) ;
bridge_channel = ast_channel_get_bridge_channel ( chan ) ;
ast_channel_unlock ( chan ) ;
if ( ! bridge_channel ) {
/* Not in a bridge */
return ;
}
ast_bridge_channel_lock_bridge ( bridge_channel ) ;
bridge = bridge_channel - > bridge ;
2013-07-24 15:38:18 +00:00
if ( bridge_channel = = bridge_find_channel ( bridge , chan ) ) {
2013-08-22 16:46:01 +00:00
/*
* XXX ASTERISK - 22366 this needs more work . The channels need
* to be made compatible again if the formats change . The
* bridge_channel thread needs to monitor for this case .
*/
2013-05-21 18:00:22 +00:00
/* The channel we want to notify is still in a bridge. */
bridge - > v_table - > notify_masquerade ( bridge , bridge_channel ) ;
2013-07-12 21:42:53 +00:00
bridge_reconfigured ( bridge , 1 ) ;
2013-05-21 18:00:22 +00:00
}
ast_bridge_unlock ( bridge ) ;
ao2_ref ( bridge_channel , - 1 ) ;
}
2016-04-19 21:58:32 +00:00
/*!
* \ brief Internal bridge impart wait condition and associated conditional .
*/
struct bridge_channel_impart_cond {
AST_LIST_ENTRY ( bridge_channel_impart_cond ) node ;
/*! Lock for the data structure */
ast_mutex_t lock ;
/*! Wait condition */
ast_cond_t cond ;
/*! Wait until done */
int done ;
} ;
AST_LIST_HEAD_NOLOCK ( bridge_channel_impart_ds_head , bridge_channel_impart_cond ) ;
/*!
* \ internal
* \ brief Signal imparting threads to wake up .
* \ since 13.9 .0
*
* \ param ds_head List of imparting threads to wake up .
*
* \ return Nothing
*/
static void bridge_channel_impart_ds_head_signal ( struct bridge_channel_impart_ds_head * ds_head )
{
if ( ds_head ) {
struct bridge_channel_impart_cond * cond ;
while ( ( cond = AST_LIST_REMOVE_HEAD ( ds_head , node ) ) ) {
ast_mutex_lock ( & cond - > lock ) ;
cond - > done = 1 ;
ast_cond_signal ( & cond - > cond ) ;
ast_mutex_unlock ( & cond - > lock ) ;
}
}
}
static void bridge_channel_impart_ds_head_dtor ( void * doomed )
{
bridge_channel_impart_ds_head_signal ( doomed ) ;
ast_free ( doomed ) ;
}
/*!
* \ internal
* \ brief Fixup the bridge impart datastore .
* \ since 13.9 .0
*
* \ param data Bridge impart datastore data to fixup from old_chan .
* \ param old_chan The datastore is moving from this channel .
* \ param new_chan The datastore is moving to this channel .
*
* \ return Nothing
*/
static void bridge_channel_impart_ds_head_fixup ( void * data , struct ast_channel * old_chan , struct ast_channel * new_chan )
{
/*
* Signal any waiting impart threads . The masquerade is going to kill
* old_chan and we don ' t need to be waiting on new_chan .
*/
bridge_channel_impart_ds_head_signal ( data ) ;
}
static const struct ast_datastore_info bridge_channel_impart_ds_info = {
. type = " bridge-impart-ds " ,
. destroy = bridge_channel_impart_ds_head_dtor ,
. chan_fixup = bridge_channel_impart_ds_head_fixup ,
} ;
/*!
* \ internal
* \ brief Add impart wait datastore conditional to channel .
* \ since 13.9 .0
*
* \ param chan Channel to add the impart wait conditional .
* \ param cond Imparting conditional to add .
*
* \ retval 0 on success .
* \ retval - 1 on error .
*/
static int bridge_channel_impart_add ( struct ast_channel * chan , struct bridge_channel_impart_cond * cond )
{
struct ast_datastore * datastore ;
struct bridge_channel_impart_ds_head * ds_head ;
ast_channel_lock ( chan ) ;
datastore = ast_channel_datastore_find ( chan , & bridge_channel_impart_ds_info , NULL ) ;
if ( ! datastore ) {
datastore = ast_datastore_alloc ( & bridge_channel_impart_ds_info , NULL ) ;
if ( ! datastore ) {
ast_channel_unlock ( chan ) ;
return - 1 ;
}
ds_head = ast_calloc ( 1 , sizeof ( * ds_head ) ) ;
if ( ! ds_head ) {
ast_channel_unlock ( chan ) ;
ast_datastore_free ( datastore ) ;
return - 1 ;
}
datastore - > data = ds_head ;
ast_channel_datastore_add ( chan , datastore ) ;
} else {
ds_head = datastore - > data ;
ast_assert ( ds_head ! = NULL ) ;
}
AST_LIST_INSERT_TAIL ( ds_head , cond , node ) ;
ast_channel_unlock ( chan ) ;
return 0 ;
}
void bridge_channel_impart_signal ( struct ast_channel * chan )
{
struct ast_datastore * datastore ;
ast_channel_lock ( chan ) ;
datastore = ast_channel_datastore_find ( chan , & bridge_channel_impart_ds_info , NULL ) ;
if ( datastore ) {
bridge_channel_impart_ds_head_signal ( datastore - > data ) ;
}
ast_channel_unlock ( chan ) ;
}
/*!
* \ internal
* \ brief Block imparting channel thread until signaled .
* \ since 13.9 .0
*
* \ param cond Imparting conditional to wait for .
*
* \ return Nothing
*/
static void bridge_channel_impart_wait ( struct bridge_channel_impart_cond * cond )
{
ast_mutex_lock ( & cond - > lock ) ;
while ( ! cond - > done ) {
ast_cond_wait ( & cond - > cond , & cond - > lock ) ;
}
ast_mutex_unlock ( & cond - > lock ) ;
}
2013-05-21 18:00:22 +00:00
/*
2013-08-15 18:20:52 +00:00
* XXX ASTERISK - 21271 make ast_bridge_join ( ) require features to be allocated just like ast_bridge_impart ( ) and not expect the struct back .
2013-05-21 18:00:22 +00:00
*
* This change is really going to break ConfBridge . All other
* users are easily changed . However , it is needed so the
* bridging code can manipulate features on all channels
* consistently no matter how they joined .
*
* Need to update the features parameter doxygen when this
* change is made to be like ast_bridge_impart ( ) .
*/
2013-07-26 16:34:56 +00:00
int ast_bridge_join ( struct ast_bridge * bridge ,
2013-05-21 18:00:22 +00:00
struct ast_channel * chan ,
struct ast_channel * swap ,
struct ast_bridge_features * features ,
struct ast_bridge_tech_optimizations * tech_args ,
2013-09-13 22:19:23 +00:00
enum ast_bridge_join_flags flags )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_channel * bridge_channel ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
int res = 0 ;
2013-05-21 18:00:22 +00:00
2013-07-25 02:20:23 +00:00
bridge_channel = bridge_channel_internal_alloc ( bridge ) ;
2013-09-13 22:19:23 +00:00
if ( flags & AST_BRIDGE_JOIN_PASS_REFERENCE ) {
2013-05-21 18:00:22 +00:00
ao2_ref ( bridge , - 1 ) ;
}
if ( ! bridge_channel ) {
2015-01-22 19:30:12 +00:00
ao2_t_cleanup ( swap , " Error exit: bridge_channel alloc failed " ) ;
2013-07-26 16:34:56 +00:00
res = - 1 ;
2013-05-21 18:00:22 +00:00
goto join_exit ;
}
2013-08-15 18:20:52 +00:00
/* XXX ASTERISK-21271 features cannot be NULL when passed in. When it is changed to allocated we can do like ast_bridge_impart() and allocate one. */
2013-05-21 18:00:22 +00:00
ast_assert ( features ! = NULL ) ;
if ( ! features ) {
ao2_ref ( bridge_channel , - 1 ) ;
2015-01-22 19:30:12 +00:00
ao2_t_cleanup ( swap , " Error exit: features is NULL " ) ;
2013-07-26 16:34:56 +00:00
res = - 1 ;
2013-05-21 18:00:22 +00:00
goto join_exit ;
}
if ( tech_args ) {
bridge_channel - > tech_args = * tech_args ;
}
ast_channel_lock ( chan ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
if ( ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_ZOMBIE ) ) {
res = - 1 ;
} else {
ast_channel_internal_bridge_channel_set ( chan , bridge_channel ) ;
}
2013-05-21 18:00:22 +00:00
ast_channel_unlock ( chan ) ;
bridge_channel - > thread = pthread_self ( ) ;
bridge_channel - > chan = chan ;
bridge_channel - > swap = swap ;
bridge_channel - > features = features ;
2013-09-13 22:19:23 +00:00
bridge_channel - > inhibit_colp = ! ! ( flags & AST_BRIDGE_JOIN_INHIBIT_JOIN_COLP ) ;
2013-05-21 18:00:22 +00:00
2015-01-29 23:03:14 +00:00
/* allow subclass to peek at upcoming push operation */
if ( bridge - > v_table - > push_peek & & ! res ) {
struct ast_bridge_channel * bcswap = NULL ;
ast_bridge_lock ( bridge ) ;
if ( bridge_channel - > swap ) {
bcswap = bridge_find_channel ( bridge , bridge_channel - > swap ) ;
}
res = bridge - > v_table - > push_peek ( bridge , bridge_channel , bcswap ) ;
ast_bridge_unlock ( bridge ) ;
}
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
if ( ! res ) {
2016-04-19 21:58:32 +00:00
res = bridge_channel_internal_join ( bridge_channel ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
}
2013-05-21 18:00:22 +00:00
/* Cleanup all the data in the bridge channel after it leaves the bridge. */
ast_channel_lock ( chan ) ;
ast_channel_internal_bridge_channel_set ( chan , NULL ) ;
ast_channel_unlock ( chan ) ;
bridge_channel - > chan = NULL ;
2015-01-22 19:30:12 +00:00
/* If bridge_channel->swap is not NULL then the join failed. */
ao2_t_cleanup ( bridge_channel - > swap , " Bridge complete: join failed " ) ;
2013-05-21 18:00:22 +00:00
bridge_channel - > swap = NULL ;
bridge_channel - > features = NULL ;
ao2_ref ( bridge_channel , - 1 ) ;
2017-09-15 14:43:21 +00:00
join_exit :
2013-07-25 02:20:23 +00:00
ast_bridge_run_after_callback ( chan ) ;
2016-04-19 21:58:32 +00:00
bridge_channel_impart_signal ( chan ) ;
2013-05-21 18:00:22 +00:00
if ( ! ( ast_channel_softhangup_internal_flag ( chan ) & AST_SOFTHANGUP_ASYNCGOTO )
2013-07-25 02:20:23 +00:00
& & ! ast_bridge_setup_after_goto ( chan ) ) {
2013-05-21 18:00:22 +00:00
/* Claim the after bridge goto is an async goto destination. */
ast_channel_lock ( chan ) ;
ast_softhangup_nolock ( chan , AST_SOFTHANGUP_ASYNCGOTO ) ;
ast_channel_unlock ( chan ) ;
}
2013-07-26 16:34:56 +00:00
return res ;
2013-05-21 18:00:22 +00:00
}
/*! \brief Thread responsible for imparted bridged channels to be departed */
static void * bridge_channel_depart_thread ( void * data )
{
2016-04-19 21:58:32 +00:00
struct ast_bridge_channel * bridge_channel = data ;
2017-09-01 10:17:02 +00:00
int res = 0 ;
2013-05-21 18:00:22 +00:00
if ( bridge_channel - > callid ) {
ast_callid_threadassoc_add ( bridge_channel - > callid ) ;
}
2017-09-01 10:17:02 +00:00
res = bridge_channel_internal_join ( bridge_channel ) ;
2013-05-21 18:00:22 +00:00
2015-01-22 19:30:12 +00:00
/*
* cleanup
*
* If bridge_channel - > swap is not NULL then the join failed .
*/
ao2_t_cleanup ( bridge_channel - > swap , " Bridge complete: Departable impart join failed " ) ;
2013-05-21 18:00:22 +00:00
bridge_channel - > swap = NULL ;
ast_bridge_features_destroy ( bridge_channel - > features ) ;
bridge_channel - > features = NULL ;
2017-09-01 10:17:02 +00:00
ast_bridge_discard_after_callback ( bridge_channel - > chan ,
res ? AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED : AST_BRIDGE_AFTER_CB_REASON_DEPART ) ;
2016-04-19 21:58:32 +00:00
/* If join failed there will be impart threads waiting. */
bridge_channel_impart_signal ( bridge_channel - > chan ) ;
2013-07-25 02:20:23 +00:00
ast_bridge_discard_after_goto ( bridge_channel - > chan ) ;
2013-05-21 18:00:22 +00:00
return NULL ;
}
/*! \brief Thread responsible for independent imparted bridged channels */
static void * bridge_channel_ind_thread ( void * data )
{
2016-04-19 21:58:32 +00:00
struct ast_bridge_channel * bridge_channel = data ;
2013-05-21 18:00:22 +00:00
struct ast_channel * chan ;
if ( bridge_channel - > callid ) {
ast_callid_threadassoc_add ( bridge_channel - > callid ) ;
}
2016-04-19 21:58:32 +00:00
bridge_channel_internal_join ( bridge_channel ) ;
2013-05-21 18:00:22 +00:00
chan = bridge_channel - > chan ;
/* cleanup */
ast_channel_lock ( chan ) ;
ast_channel_internal_bridge_channel_set ( chan , NULL ) ;
ast_channel_unlock ( chan ) ;
bridge_channel - > chan = NULL ;
2015-01-22 19:30:12 +00:00
/* If bridge_channel->swap is not NULL then the join failed. */
ao2_t_cleanup ( bridge_channel - > swap , " Bridge complete: Independent impart join failed " ) ;
2013-05-21 18:00:22 +00:00
bridge_channel - > swap = NULL ;
ast_bridge_features_destroy ( bridge_channel - > features ) ;
bridge_channel - > features = NULL ;
ao2_ref ( bridge_channel , - 1 ) ;
2013-07-25 02:20:23 +00:00
ast_bridge_run_after_callback ( chan ) ;
2016-04-19 21:58:32 +00:00
/* If join failed there will be impart threads waiting. */
bridge_channel_impart_signal ( chan ) ;
2013-07-25 02:20:23 +00:00
ast_bridge_run_after_goto ( chan ) ;
2013-05-21 18:00:22 +00:00
return NULL ;
}
2016-04-19 21:58:32 +00:00
static int bridge_impart_internal ( struct ast_bridge * bridge ,
2013-09-13 22:19:23 +00:00
struct ast_channel * chan ,
struct ast_channel * swap ,
struct ast_bridge_features * features ,
2016-04-19 21:58:32 +00:00
enum ast_bridge_impart_flags flags ,
struct bridge_channel_impart_cond * cond )
2013-05-21 18:00:22 +00:00
{
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
int res = 0 ;
2013-05-21 18:00:22 +00:00
struct ast_bridge_channel * bridge_channel ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
/* Imparted channels cannot have a PBX. */
if ( ast_channel_pbx ( chan ) ) {
ast_log ( AST_LOG_WARNING , " Channel %s has a PBX thread and cannot be imparted into bridge %s \n " ,
ast_channel_name ( chan ) , bridge - > uniqueid ) ;
2014-10-28 16:41:17 +00:00
ast_bridge_features_destroy ( features ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
return - 1 ;
}
2013-05-21 18:00:22 +00:00
/* Supply an empty features structure if the caller did not. */
if ( ! features ) {
features = ast_bridge_features_new ( ) ;
if ( ! features ) {
return - 1 ;
}
}
/* Try to allocate a structure for the bridge channel */
2013-07-25 02:20:23 +00:00
bridge_channel = bridge_channel_internal_alloc ( bridge ) ;
2013-05-21 18:00:22 +00:00
if ( ! bridge_channel ) {
ast_bridge_features_destroy ( features ) ;
return - 1 ;
}
ast_channel_lock ( chan ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
if ( ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_ZOMBIE ) ) {
ast_log ( AST_LOG_NOTICE , " Channel %s is a zombie and cannot be imparted into bridge %s \n " ,
ast_channel_name ( chan ) , bridge - > uniqueid ) ;
res = - 1 ;
} else {
ast_channel_internal_bridge_channel_set ( chan , bridge_channel ) ;
}
2013-05-21 18:00:22 +00:00
ast_channel_unlock ( chan ) ;
bridge_channel - > chan = chan ;
2015-01-22 19:30:12 +00:00
bridge_channel - > swap = ao2_t_bump ( swap , " Setting up bridge impart " ) ;
2013-05-21 18:00:22 +00:00
bridge_channel - > features = features ;
2013-09-13 22:19:23 +00:00
bridge_channel - > inhibit_colp = ! ! ( flags & AST_BRIDGE_IMPART_INHIBIT_JOIN_COLP ) ;
bridge_channel - > depart_wait =
( flags & AST_BRIDGE_IMPART_CHAN_MASK ) = = AST_BRIDGE_IMPART_CHAN_DEPARTABLE ;
2013-05-21 18:00:22 +00:00
bridge_channel - > callid = ast_read_threadstorage_callid ( ) ;
2015-01-29 23:03:14 +00:00
/* allow subclass to peek at swap channel before it can hangup */
if ( bridge - > v_table - > push_peek & & ! res ) {
struct ast_bridge_channel * bcswap = NULL ;
ast_bridge_lock ( bridge ) ;
if ( bridge_channel - > swap ) {
bcswap = bridge_find_channel ( bridge , bridge_channel - > swap ) ;
}
res = bridge - > v_table - > push_peek ( bridge , bridge_channel , bcswap ) ;
ast_bridge_unlock ( bridge ) ;
}
2013-05-21 18:00:22 +00:00
/* Actually create the thread that will handle the channel */
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
if ( ! res ) {
2016-04-19 21:58:32 +00:00
res = bridge_channel_impart_add ( chan , cond ) ;
}
if ( ! res ) {
2013-09-13 22:19:23 +00:00
if ( ( flags & AST_BRIDGE_IMPART_CHAN_MASK ) = = AST_BRIDGE_IMPART_CHAN_INDEPENDENT ) {
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
res = ast_pthread_create_detached ( & bridge_channel - > thread , NULL ,
2016-04-19 21:58:32 +00:00
bridge_channel_ind_thread , bridge_channel ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
} else {
res = ast_pthread_create ( & bridge_channel - > thread , NULL ,
2016-04-19 21:58:32 +00:00
bridge_channel_depart_thread , bridge_channel ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
}
2015-07-08 19:56:10 +00:00
if ( ! res ) {
2016-04-19 21:58:32 +00:00
bridge_channel_impart_wait ( cond ) ;
2015-07-08 19:56:10 +00:00
}
2013-05-21 18:00:22 +00:00
}
if ( res ) {
/* cleanup */
ast_channel_lock ( chan ) ;
ast_channel_internal_bridge_channel_set ( chan , NULL ) ;
ast_channel_unlock ( chan ) ;
bridge_channel - > chan = NULL ;
2015-01-22 19:30:12 +00:00
ao2_t_cleanup ( bridge_channel - > swap , " Bridge complete: Impart failed " ) ;
2013-05-21 18:00:22 +00:00
bridge_channel - > swap = NULL ;
ast_bridge_features_destroy ( bridge_channel - > features ) ;
bridge_channel - > features = NULL ;
ao2_ref ( bridge_channel , - 1 ) ;
return - 1 ;
}
return 0 ;
}
2016-04-19 21:58:32 +00:00
int ast_bridge_impart ( struct ast_bridge * bridge ,
struct ast_channel * chan ,
struct ast_channel * swap ,
struct ast_bridge_features * features ,
enum ast_bridge_impart_flags flags )
{
struct bridge_channel_impart_cond cond = {
. done = 0 ,
} ;
int res ;
ast_mutex_init ( & cond . lock ) ;
ast_cond_init ( & cond . cond , NULL ) ;
res = bridge_impart_internal ( bridge , chan , swap , features , flags , & cond ) ;
if ( res ) {
/* Impart failed. Signal any other waiting impart threads */
bridge_channel_impart_signal ( chan ) ;
}
ast_cond_destroy ( & cond . cond ) ;
ast_mutex_destroy ( & cond . lock ) ;
return res ;
}
2013-05-21 18:00:22 +00:00
int ast_bridge_depart ( struct ast_channel * chan )
{
struct ast_bridge_channel * bridge_channel ;
int departable ;
ast_channel_lock ( chan ) ;
bridge_channel = ast_channel_internal_bridge_channel ( chan ) ;
departable = bridge_channel & & bridge_channel - > depart_wait ;
ast_channel_unlock ( chan ) ;
if ( ! departable ) {
ast_log ( LOG_ERROR , " Channel %s cannot be departed. \n " ,
ast_channel_name ( chan ) ) ;
/*
* Should never happen . It likely means that
* ast_bridge_depart ( ) is called by two threads for the same
* channel , the channel was never imparted to be departed , or it
* has already been departed .
*/
ast_assert ( 0 ) ;
return - 1 ;
}
/*
2014-11-24 20:32:29 +00:00
* We are claiming the bridge_channel reference held by
* bridge_channel_depart_thread ( ) .
2013-05-21 18:00:22 +00:00
*/
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , AST_CAUSE_NORMAL_CLEARING ) ;
2013-05-21 18:00:22 +00:00
/* Wait for the depart thread to die */
ast_debug ( 1 , " Waiting for %p(%s) bridge thread to die. \n " ,
bridge_channel , ast_channel_name ( bridge_channel - > chan ) ) ;
pthread_join ( bridge_channel - > thread , NULL ) ;
ast_channel_lock ( chan ) ;
ast_channel_internal_bridge_channel_set ( chan , NULL ) ;
ast_channel_unlock ( chan ) ;
/* We can get rid of the bridge_channel after the depart thread has died. */
ao2_ref ( bridge_channel , - 1 ) ;
return 0 ;
}
int ast_bridge_remove ( struct ast_bridge * bridge , struct ast_channel * chan )
{
struct ast_bridge_channel * bridge_channel ;
ast_bridge_lock ( bridge ) ;
/* Try to find the channel that we want to remove */
2013-07-24 15:38:18 +00:00
if ( ! ( bridge_channel = bridge_find_channel ( bridge , chan ) ) ) {
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
return - 1 ;
}
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , AST_CAUSE_NORMAL_CLEARING ) ;
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
return 0 ;
}
2013-08-16 20:48:13 +00:00
static void kick_it ( struct ast_bridge_channel * bridge_channel , const void * payload , size_t payload_size )
{
2013-08-22 21:09:52 +00:00
ast_bridge_channel_kick ( bridge_channel , AST_CAUSE_NORMAL_CLEARING ) ;
2013-08-16 20:48:13 +00:00
}
int ast_bridge_kick ( struct ast_bridge * bridge , struct ast_channel * chan )
{
struct ast_bridge_channel * bridge_channel ;
int res ;
ast_bridge_lock ( bridge ) ;
/* Try to find the channel that we want to kick. */
if ( ! ( bridge_channel = bridge_find_channel ( bridge , chan ) ) ) {
ast_bridge_unlock ( bridge ) ;
return - 1 ;
}
2013-08-21 15:51:19 +00:00
res = ast_bridge_channel_queue_callback ( bridge_channel , 0 , kick_it , NULL , 0 ) ;
2013-08-16 20:48:13 +00:00
ast_bridge_unlock ( bridge ) ;
return res ;
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Point the bridge_channel to a new bridge .
* \ since 12.0 .0
*
* \ param bridge_channel What is to point to a new bridge .
* \ param new_bridge Where the bridge channel should point .
*
* \ return Nothing
*/
static void bridge_channel_change_bridge ( struct ast_bridge_channel * bridge_channel , struct ast_bridge * new_bridge )
{
struct ast_bridge * old_bridge ;
ao2_ref ( new_bridge , + 1 ) ;
ast_bridge_channel_lock ( bridge_channel ) ;
ast_channel_lock ( bridge_channel - > chan ) ;
old_bridge = bridge_channel - > bridge ;
bridge_channel - > bridge = new_bridge ;
ast_channel_internal_bridge_set ( bridge_channel - > chan , new_bridge ) ;
ast_channel_unlock ( bridge_channel - > chan ) ;
ast_bridge_channel_unlock ( bridge_channel ) ;
ao2_ref ( old_bridge , - 1 ) ;
}
2014-08-07 15:30:19 +00:00
static void bridge_channel_moving ( struct ast_bridge_channel * bridge_channel , struct ast_bridge * src , struct ast_bridge * dst )
{
struct ast_bridge_features * features = bridge_channel - > features ;
struct ast_bridge_hook * hook ;
struct ao2_iterator iter ;
/* Run any moving hooks. */
iter = ao2_iterator_init ( features - > other_hooks , 0 ) ;
for ( ; ( hook = ao2_iterator_next ( & iter ) ) ; ao2_ref ( hook , - 1 ) ) {
int remove_me ;
ast_bridge_move_indicate_callback move_cb ;
if ( hook - > type ! = AST_BRIDGE_HOOK_TYPE_MOVE ) {
continue ;
}
move_cb = ( ast_bridge_move_indicate_callback ) hook - > callback ;
remove_me = move_cb ( bridge_channel , hook - > hook_pvt , src , dst ) ;
if ( remove_me ) {
ast_debug ( 1 , " Move detection hook %p is being removed from %p(%s) \n " ,
hook , bridge_channel , ast_channel_name ( bridge_channel - > chan ) ) ;
ao2_unlink ( features - > other_hooks , hook ) ;
}
}
ao2_iterator_destroy ( & iter ) ;
}
2013-07-24 15:38:18 +00:00
void bridge_do_merge ( struct ast_bridge * dst_bridge , struct ast_bridge * src_bridge , struct ast_bridge_channel * * kick_me , unsigned int num_kick ,
2013-07-12 21:42:53 +00:00
unsigned int optimized )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_channel * bridge_channel ;
unsigned int idx ;
ast_debug ( 1 , " Merging bridge %s into bridge %s \n " ,
src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
ast_bridge_publish_merge ( dst_bridge , src_bridge ) ;
/*
* Move channels from src_bridge over to dst_bridge .
*
* We must use AST_LIST_TRAVERSE_SAFE_BEGIN ( ) because
2013-07-25 02:20:23 +00:00
* bridge_channel_internal_pull ( ) alters the list we are traversing .
2013-05-21 18:00:22 +00:00
*/
AST_LIST_TRAVERSE_SAFE_BEGIN ( & src_bridge - > channels , bridge_channel , entry ) {
2013-07-25 02:20:23 +00:00
if ( bridge_channel - > state ! = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-05-21 18:00:22 +00:00
/*
* The channel is already leaving let it leave normally because
* pulling it may delete hooks that should run for this channel .
*/
continue ;
}
if ( ast_test_flag ( & bridge_channel - > features - > feature_flags ,
AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE ) ) {
continue ;
}
if ( kick_me ) {
for ( idx = 0 ; idx < num_kick ; + + idx ) {
if ( bridge_channel = = kick_me [ idx ] ) {
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , AST_CAUSE_NORMAL_CLEARING ) ;
2013-05-21 18:00:22 +00:00
break ;
}
}
}
2013-07-25 02:20:23 +00:00
bridge_channel_internal_pull ( bridge_channel ) ;
if ( bridge_channel - > state ! = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-05-21 18:00:22 +00:00
/*
* The channel died as a result of being pulled or it was
* kicked . Leave it pointing to the original bridge .
*/
continue ;
}
2014-08-07 15:30:19 +00:00
bridge_channel_moving ( bridge_channel , bridge_channel - > bridge , dst_bridge ) ;
2013-05-21 18:00:22 +00:00
/* Point to new bridge.*/
bridge_channel_change_bridge ( bridge_channel , dst_bridge ) ;
2013-07-25 02:20:23 +00:00
if ( bridge_channel_internal_push ( bridge_channel ) ) {
2013-10-22 17:06:21 +00:00
ast_bridge_features_remove ( bridge_channel - > features ,
AST_BRIDGE_HOOK_REMOVE_ON_PULL ) ;
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , bridge_channel - > bridge - > cause ) ;
2013-05-21 18:00:22 +00:00
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( kick_me ) {
/*
* Now we can kick any channels in the dst_bridge without
* potentially dissolving the bridge .
*/
for ( idx = 0 ; idx < num_kick ; + + idx ) {
bridge_channel = kick_me [ idx ] ;
ast_bridge_channel_lock ( bridge_channel ) ;
2013-07-25 02:20:23 +00:00
if ( bridge_channel - > state = = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge_nolock ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , AST_CAUSE_NORMAL_CLEARING ) ;
2013-07-25 02:20:23 +00:00
bridge_channel_internal_pull ( bridge_channel ) ;
2013-05-21 18:00:22 +00:00
}
ast_bridge_channel_unlock ( bridge_channel ) ;
}
}
2013-07-12 21:42:53 +00:00
bridge_reconfigured ( dst_bridge , ! optimized ) ;
bridge_reconfigured ( src_bridge , ! optimized ) ;
2013-05-21 18:00:22 +00:00
ast_debug ( 1 , " Merged bridge %s into bridge %s \n " ,
src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
}
struct merge_direction {
/*! Destination merge bridge. */
struct ast_bridge * dest ;
/*! Source merge bridge. */
struct ast_bridge * src ;
} ;
/*!
* \ internal
* \ brief Determine which bridge should merge into the other .
* \ since 12.0 .0
*
* \ param bridge1 A bridge for merging
* \ param bridge2 A bridge for merging
*
* \ note The two bridges are assumed already locked .
*
* \ return Which bridge merges into which or NULL bridges if cannot merge .
*/
static struct merge_direction bridge_merge_determine_direction ( struct ast_bridge * bridge1 , struct ast_bridge * bridge2 )
{
struct merge_direction merge = { NULL , NULL } ;
int bridge1_priority ;
int bridge2_priority ;
if ( ! ast_test_flag ( & bridge1 - > feature_flags ,
AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM )
& & ! ast_test_flag ( & bridge2 - > feature_flags ,
AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM ) ) {
/*
* Can merge either way . Merge to the higher priority merge
* bridge . Otherwise merge to the larger bridge .
*/
bridge1_priority = bridge1 - > v_table - > get_merge_priority ( bridge1 ) ;
bridge2_priority = bridge2 - > v_table - > get_merge_priority ( bridge2 ) ;
if ( bridge2_priority < bridge1_priority ) {
merge . dest = bridge1 ;
merge . src = bridge2 ;
} else if ( bridge1_priority < bridge2_priority ) {
merge . dest = bridge2 ;
merge . src = bridge1 ;
} else {
/* Merge to the larger bridge. */
if ( bridge2 - > num_channels < = bridge1 - > num_channels ) {
merge . dest = bridge1 ;
merge . src = bridge2 ;
} else {
merge . dest = bridge2 ;
merge . src = bridge1 ;
}
}
} else if ( ! ast_test_flag ( & bridge1 - > feature_flags , AST_BRIDGE_FLAG_MERGE_INHIBIT_TO )
& & ! ast_test_flag ( & bridge2 - > feature_flags , AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM ) ) {
/* Can merge only one way. */
merge . dest = bridge1 ;
merge . src = bridge2 ;
} else if ( ! ast_test_flag ( & bridge2 - > feature_flags , AST_BRIDGE_FLAG_MERGE_INHIBIT_TO )
& & ! ast_test_flag ( & bridge1 - > feature_flags , AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM ) ) {
/* Can merge only one way. */
merge . dest = bridge2 ;
merge . src = bridge1 ;
}
return merge ;
}
/*!
* \ internal
* \ brief Merge two bridges together
* \ since 12.0 .0
*
* \ param dst_bridge Destination bridge of merge .
* \ param src_bridge Source bridge of merge .
* \ param merge_best_direction TRUE if don ' t care about which bridge merges into the other .
* \ param kick_me Array of channels to kick from the bridges .
* \ param num_kick Number of channels in the kick_me array .
*
* \ note The dst_bridge and src_bridge are assumed already locked .
*
* \ retval 0 on success
* \ retval - 1 on failure
*/
static int bridge_merge_locked ( struct ast_bridge * dst_bridge , struct ast_bridge * src_bridge , int merge_best_direction , struct ast_channel * * kick_me , unsigned int num_kick )
{
struct merge_direction merge ;
struct ast_bridge_channel * * kick_them = NULL ;
/* Sanity check. */
ast_assert ( dst_bridge & & src_bridge & & dst_bridge ! = src_bridge & & ( ! num_kick | | kick_me ) ) ;
if ( dst_bridge - > dissolved | | src_bridge - > dissolved ) {
ast_debug ( 1 , " Can't merge bridges %s and %s, at least one bridge is dissolved. \n " ,
src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( ast_test_flag ( & dst_bridge - > feature_flags , AST_BRIDGE_FLAG_MASQUERADE_ONLY )
| | ast_test_flag ( & src_bridge - > feature_flags , AST_BRIDGE_FLAG_MASQUERADE_ONLY ) ) {
ast_debug ( 1 , " Can't merge bridges %s and %s, masquerade only. \n " ,
src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( dst_bridge - > inhibit_merge | | src_bridge - > inhibit_merge ) {
ast_debug ( 1 , " Can't merge bridges %s and %s, merging temporarily inhibited. \n " ,
src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( merge_best_direction ) {
merge = bridge_merge_determine_direction ( dst_bridge , src_bridge ) ;
} else {
merge . dest = dst_bridge ;
merge . src = src_bridge ;
}
if ( ! merge . dest
| | ast_test_flag ( & merge . dest - > feature_flags , AST_BRIDGE_FLAG_MERGE_INHIBIT_TO )
| | ast_test_flag ( & merge . src - > feature_flags , AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM ) ) {
ast_debug ( 1 , " Can't merge bridges %s and %s, merging inhibited. \n " ,
src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( merge . src - > num_channels < 2 ) {
/*
* For a two party bridge , a channel may be temporarily removed
* from the source bridge or the initial bridge members have not
* joined yet .
*/
ast_debug ( 1 , " Can't merge bridge %s into bridge %s, not enough channels in source bridge. \n " ,
merge . src - > uniqueid , merge . dest - > uniqueid ) ;
return - 1 ;
}
if ( 2 + num_kick < merge . dest - > num_channels + merge . src - > num_channels
& & ! ( merge . dest - > technology - > capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX )
& & ( ! ast_test_flag ( & merge . dest - > feature_flags , AST_BRIDGE_FLAG_SMART )
| | ! ( merge . dest - > allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX ) ) ) {
ast_debug ( 1 , " Can't merge bridge %s into bridge %s, multimix is needed and it cannot be acquired. \n " ,
merge . src - > uniqueid , merge . dest - > uniqueid ) ;
return - 1 ;
}
if ( num_kick ) {
unsigned int num_to_kick = 0 ;
unsigned int idx ;
kick_them = ast_alloca ( num_kick * sizeof ( * kick_them ) ) ;
for ( idx = 0 ; idx < num_kick ; + + idx ) {
2013-07-24 15:38:18 +00:00
kick_them [ num_to_kick ] = bridge_find_channel ( merge . src , kick_me [ idx ] ) ;
2013-05-21 18:00:22 +00:00
if ( ! kick_them [ num_to_kick ] ) {
2013-07-24 15:38:18 +00:00
kick_them [ num_to_kick ] = bridge_find_channel ( merge . dest , kick_me [ idx ] ) ;
2013-05-21 18:00:22 +00:00
}
if ( kick_them [ num_to_kick ] ) {
+ + num_to_kick ;
}
}
if ( num_to_kick ! = num_kick ) {
ast_debug ( 1 , " Can't merge bridge %s into bridge %s, at least one kicked channel is not in either bridge. \n " ,
merge . src - > uniqueid , merge . dest - > uniqueid ) ;
return - 1 ;
}
}
2013-07-24 15:38:18 +00:00
bridge_do_merge ( merge . dest , merge . src , kick_them , num_kick , 0 ) ;
2013-05-21 18:00:22 +00:00
return 0 ;
}
int ast_bridge_merge ( struct ast_bridge * dst_bridge , struct ast_bridge * src_bridge , int merge_best_direction , struct ast_channel * * kick_me , unsigned int num_kick )
{
int res ;
/* Sanity check. */
ast_assert ( dst_bridge & & src_bridge ) ;
ast_bridge_lock_both ( dst_bridge , src_bridge ) ;
res = bridge_merge_locked ( dst_bridge , src_bridge , merge_best_direction , kick_me , num_kick ) ;
ast_bridge_unlock ( src_bridge ) ;
ast_bridge_unlock ( dst_bridge ) ;
return res ;
}
2013-07-24 15:38:18 +00:00
int bridge_do_move ( struct ast_bridge * dst_bridge , struct ast_bridge_channel * bridge_channel , int attempt_recovery ,
2013-07-12 21:42:53 +00:00
unsigned int optimized )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge * orig_bridge ;
int was_in_bridge ;
int res = 0 ;
if ( bridge_channel - > swap ) {
ast_debug ( 1 , " Moving %p(%s) into bridge %s swapping with %s \n " ,
bridge_channel , ast_channel_name ( bridge_channel - > chan ) , dst_bridge - > uniqueid ,
ast_channel_name ( bridge_channel - > swap ) ) ;
} else {
ast_debug ( 1 , " Moving %p(%s) into bridge %s \n " ,
bridge_channel , ast_channel_name ( bridge_channel - > chan ) , dst_bridge - > uniqueid ) ;
}
orig_bridge = bridge_channel - > bridge ;
was_in_bridge = bridge_channel - > in_bridge ;
2013-07-25 02:20:23 +00:00
bridge_channel_internal_pull ( bridge_channel ) ;
if ( bridge_channel - > state ! = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-05-21 18:00:22 +00:00
/*
* The channel died as a result of being pulled . Leave it
* pointing to the original bridge .
2015-01-22 19:30:12 +00:00
*
* Clear out the swap channel pointer . A ref is not held
* by bridge_channel - > swap at this point .
2013-05-21 18:00:22 +00:00
*/
2015-01-22 19:30:12 +00:00
bridge_channel - > swap = NULL ;
2013-07-12 21:42:53 +00:00
bridge_reconfigured ( orig_bridge , 0 ) ;
2013-05-21 18:00:22 +00:00
return - 1 ;
}
/* Point to new bridge.*/
ao2_ref ( orig_bridge , + 1 ) ; /* Keep a ref in case the push fails. */
bridge_channel_change_bridge ( bridge_channel , dst_bridge ) ;
2014-08-07 15:30:19 +00:00
bridge_channel_moving ( bridge_channel , orig_bridge , dst_bridge ) ;
2016-02-20 01:06:14 +00:00
if ( bridge_channel_internal_push_full ( bridge_channel , optimized ) ) {
2013-05-21 18:00:22 +00:00
/* Try to put the channel back into the original bridge. */
2013-10-22 17:06:21 +00:00
ast_bridge_features_remove ( bridge_channel - > features ,
AST_BRIDGE_HOOK_REMOVE_ON_PULL ) ;
2013-05-21 18:00:22 +00:00
if ( attempt_recovery & & was_in_bridge ) {
/* Point back to original bridge. */
bridge_channel_change_bridge ( bridge_channel , orig_bridge ) ;
2013-07-25 02:20:23 +00:00
if ( bridge_channel_internal_push ( bridge_channel ) ) {
2013-10-22 17:06:21 +00:00
ast_bridge_features_remove ( bridge_channel - > features ,
AST_BRIDGE_HOOK_REMOVE_ON_PULL ) ;
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , bridge_channel - > bridge - > cause ) ;
2013-05-21 18:00:22 +00:00
}
} else {
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , bridge_channel - > bridge - > cause ) ;
2013-08-23 18:33:36 +00:00
bridge_channel_settle_owed_events ( orig_bridge , bridge_channel ) ;
2013-05-21 18:00:22 +00:00
}
res = - 1 ;
2016-02-20 01:06:14 +00:00
} else if ( ! optimized ) {
2013-08-23 18:33:36 +00:00
bridge_channel_settle_owed_events ( orig_bridge , bridge_channel ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-12 21:42:53 +00:00
bridge_reconfigured ( dst_bridge , ! optimized ) ;
bridge_reconfigured ( orig_bridge , ! optimized ) ;
2013-05-21 18:00:22 +00:00
ao2_ref ( orig_bridge , - 1 ) ;
return res ;
}
/*!
* \ internal
* \ brief Move a channel from one bridge to another .
* \ since 12.0 .0
*
* \ param dst_bridge Destination bridge of bridge channel move .
* \ param src_bridge Source bridge of bridge channel move .
* \ param chan Channel to move .
* \ param swap Channel to replace in dst_bridge .
* \ param attempt_recovery TRUE if failure attempts to push channel back into original bridge .
*
* \ note The dst_bridge and src_bridge are assumed already locked .
*
* \ retval 0 on success .
* \ retval - 1 on failure .
*/
static int bridge_move_locked ( struct ast_bridge * dst_bridge , struct ast_bridge * src_bridge , struct ast_channel * chan , struct ast_channel * swap , int attempt_recovery )
{
struct ast_bridge_channel * bridge_channel ;
if ( dst_bridge - > dissolved | | src_bridge - > dissolved ) {
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, at least one bridge is dissolved. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( ast_test_flag ( & dst_bridge - > feature_flags , AST_BRIDGE_FLAG_MASQUERADE_ONLY )
| | ast_test_flag ( & src_bridge - > feature_flags , AST_BRIDGE_FLAG_MASQUERADE_ONLY ) ) {
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, masquerade only. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( dst_bridge - > inhibit_merge | | src_bridge - > inhibit_merge ) {
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, temporarily inhibited. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
2013-07-24 15:38:18 +00:00
bridge_channel = bridge_find_channel ( src_bridge , chan ) ;
2013-05-21 18:00:22 +00:00
if ( ! bridge_channel ) {
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, channel not in bridge. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
2013-07-25 02:20:23 +00:00
if ( bridge_channel - > state ! = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-05-21 18:00:22 +00:00
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, channel leaving bridge. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( ast_test_flag ( & bridge_channel - > features - > feature_flags ,
AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE ) ) {
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, channel immovable. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ) ;
return - 1 ;
}
if ( swap ) {
struct ast_bridge_channel * bridge_channel_swap ;
2013-07-24 15:38:18 +00:00
bridge_channel_swap = bridge_find_channel ( dst_bridge , swap ) ;
2013-05-21 18:00:22 +00:00
if ( ! bridge_channel_swap ) {
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, swap channel %s not in bridge. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ,
ast_channel_name ( swap ) ) ;
return - 1 ;
}
2013-07-25 02:20:23 +00:00
if ( bridge_channel_swap - > state ! = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-05-21 18:00:22 +00:00
ast_debug ( 1 , " Can't move channel %s from bridge %s into bridge %s, swap channel %s leaving bridge. \n " ,
ast_channel_name ( chan ) , src_bridge - > uniqueid , dst_bridge - > uniqueid ,
ast_channel_name ( swap ) ) ;
return - 1 ;
}
}
bridge_channel - > swap = swap ;
2013-07-24 15:38:18 +00:00
return bridge_do_move ( dst_bridge , bridge_channel , attempt_recovery , 0 ) ;
2013-05-21 18:00:22 +00:00
}
int ast_bridge_move ( struct ast_bridge * dst_bridge , struct ast_bridge * src_bridge , struct ast_channel * chan , struct ast_channel * swap , int attempt_recovery )
{
int res ;
ast_bridge_lock_both ( dst_bridge , src_bridge ) ;
res = bridge_move_locked ( dst_bridge , src_bridge , chan , swap , attempt_recovery ) ;
ast_bridge_unlock ( src_bridge ) ;
ast_bridge_unlock ( dst_bridge ) ;
return res ;
}
2013-06-25 22:28:22 +00:00
int ast_bridge_add_channel ( struct ast_bridge * bridge , struct ast_channel * chan ,
2013-06-26 14:38:57 +00:00
struct ast_bridge_features * features , int play_tone , const char * xfersound )
2013-06-25 22:28:22 +00:00
{
RAII_VAR ( struct ast_bridge * , chan_bridge , NULL , ao2_cleanup ) ;
2013-06-26 14:38:57 +00:00
RAII_VAR ( struct ast_channel * , yanked_chan , NULL , ao2_cleanup ) ;
2013-06-25 22:28:22 +00:00
ast_channel_lock ( chan ) ;
chan_bridge = ast_channel_get_bridge ( chan ) ;
ast_channel_unlock ( chan ) ;
if ( chan_bridge ) {
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
struct ast_bridge_channel * bridge_channel ;
2013-06-25 22:28:22 +00:00
2016-04-13 18:50:04 +00:00
/* The channel is in a bridge so it is not getting any new features. */
ast_bridge_features_destroy ( features ) ;
2013-06-26 14:38:57 +00:00
ast_bridge_lock_both ( bridge , chan_bridge ) ;
2013-07-24 15:38:18 +00:00
bridge_channel = bridge_find_channel ( chan_bridge , chan ) ;
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
2013-06-26 14:38:57 +00:00
if ( bridge_move_locked ( bridge , chan_bridge , chan , NULL , 1 ) ) {
ast_bridge_unlock ( chan_bridge ) ;
ast_bridge_unlock ( bridge ) ;
2013-06-25 22:28:22 +00:00
return - 1 ;
}
2013-06-26 14:38:57 +00:00
/*
* bridge_move_locked ( ) will implicitly ensure that
* bridge_channel is not NULL .
*/
ast_assert ( bridge_channel ! = NULL ) ;
2013-06-25 22:28:22 +00:00
2013-06-26 14:38:57 +00:00
/*
* Additional checks if the channel we just stole dissolves the
* original bridge .
*/
bridge_dissolve_check_stolen ( chan_bridge , bridge_channel ) ;
ast_bridge_unlock ( chan_bridge ) ;
ast_bridge_unlock ( bridge ) ;
2013-06-25 22:28:22 +00:00
} else {
/* Slightly less easy case. We need to yank channel A from
* where he currently is and impart him into our bridge .
*/
2013-06-26 14:38:57 +00:00
yanked_chan = ast_channel_yank ( chan ) ;
if ( ! yanked_chan ) {
2013-06-25 22:28:22 +00:00
ast_log ( LOG_WARNING , " Could not gain control of channel %s \n " , ast_channel_name ( chan ) ) ;
2016-04-13 18:50:04 +00:00
ast_bridge_features_destroy ( features ) ;
2013-06-25 22:28:22 +00:00
return - 1 ;
}
2013-06-26 14:38:57 +00:00
if ( ast_channel_state ( yanked_chan ) ! = AST_STATE_UP ) {
ast_answer ( yanked_chan ) ;
2013-06-25 22:28:22 +00:00
}
2013-06-26 14:38:57 +00:00
ast_channel_ref ( yanked_chan ) ;
2013-09-13 22:19:23 +00:00
if ( ast_bridge_impart ( bridge , yanked_chan , NULL , features ,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT ) ) {
Fix two race conditions and ref counting issue when joining a bridge
These problems were all caught by a test in the Asterisk Test Suite that
originated some Local channels and attempted to move the ;2 half of the Local
channel into a bridge using the Bridge AMI action.
(1) When originating a channel, the Newchannel event is emitted quickly;
however, the ;2 channel will not have a pbx thread assigned to it until
after the outbound 'dialing' for the ;1 is complete. Thus, there is a period
of time where the outside world "knows" of the channel's existence and can
influence it but Asterisk has not yet started the dialplan execution thread.
If a Bridge AMI action is taken on the channel, the channel appears to be a
Dialed channel with no PBX thread; hence, the channel will be imparted into
the Bridge by first 'yanking' the channel. At the same time, a race condition
can occur after the yank (but before entering the bridge) when ;1 answers
and starts a PBX on the ;2. The end result currently is an assertion failure
in the Bridging API, as a channel with a PBX is imparted into the Bridge.
There's no way to prevent AMI from attempting to Bridge a channel
immediately after creation; likewise, holding the channel lock through the
entire Dial operation is unwise (and impossible). Instead of treating the
presence of a PBX thread as an error, we simply bail out of the adding the
channel to the bridge through ast_bridge_impart. The Bridge action will
then fail - but we avoid a situation where the channel is both executing
a PBX thread and simultaneously being given a separate thread in the
bridging system (which would be a "bad thing"). Since imparting a channel
with a PBX *can* occur and is not a programming error, the asserts have been
removed.
(2) When the first condition occurs, we have to take one of two actions: either
hangup the yanked channel as it did not enter the bridge, or deref it
because we don't own it. We can determine if we own it or not by testing
for the presence of the PBX thread. If we hung it up directly, we'd crash.
(3) bridge_find_channel does not increase the reference count of the
ast_bridge_channel object. The RAII_VAR usage in ast_bridge_add_channel
thus created a ticking time bomb in whatever bridge the channel moved into,
as the destructor for the ast_bridge_channel object would be called.
Review: https://reviewboard.asterisk.org/r/2741/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396543 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-08-12 15:59:19 +00:00
/* It is possible for us to yank a channel and have some other
* thread start a PBX on the channl after we yanked it . In particular ,
* this can theoretically happen on the ; 2 of a Local channel if we
* yank it prior to the ; 1 being answered . Make sure that it isn ' t
* executing a PBX before hanging it up .
*/
if ( ast_channel_pbx ( yanked_chan ) ) {
ast_channel_unref ( yanked_chan ) ;
} else {
ast_hangup ( yanked_chan ) ;
}
2013-06-25 22:28:22 +00:00
return - 1 ;
}
}
if ( play_tone & & ! ast_strlen_zero ( xfersound ) ) {
2013-06-26 14:38:57 +00:00
struct ast_channel * play_chan = yanked_chan ? : chan ;
2013-06-25 22:28:22 +00:00
RAII_VAR ( struct ast_bridge_channel * , play_bridge_channel , NULL , ao2_cleanup ) ;
ast_channel_lock ( play_chan ) ;
play_bridge_channel = ast_channel_get_bridge_channel ( play_chan ) ;
ast_channel_unlock ( play_chan ) ;
if ( ! play_bridge_channel ) {
2013-06-26 14:38:57 +00:00
ast_log ( LOG_WARNING , " Unable to play tone for channel %s. No longer in a bridge. \n " ,
ast_channel_name ( play_chan ) ) ;
2013-06-25 22:28:22 +00:00
} else {
ast_bridge_channel_queue_playfile ( play_bridge_channel , NULL , xfersound , NULL ) ;
}
}
return 0 ;
}
2013-05-28 14:45:31 +00:00
static int bridge_allows_optimization ( struct ast_bridge * bridge )
{
return ! ( bridge - > inhibit_merge
| | bridge - > dissolved
| | ast_test_flag ( & bridge - > feature_flags , AST_BRIDGE_FLAG_MASQUERADE_ONLY ) ) ;
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Lock the unreal channel stack for chan and prequalify it .
* \ since 12.0 .0
*
* \ param chan Unreal channel writing a frame into the channel driver .
*
* \ note It is assumed that chan is already locked .
*
* \ retval bridge on success with bridge and bridge_channel locked .
* \ retval NULL if cannot do optimization now .
*/
static struct ast_bridge * optimize_lock_chan_stack ( struct ast_channel * chan )
{
struct ast_bridge * bridge ;
struct ast_bridge_channel * bridge_channel ;
if ( ! AST_LIST_EMPTY ( ast_channel_readq ( chan ) ) ) {
return NULL ;
}
2013-08-08 12:38:06 +00:00
if ( ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_EMULATE_DTMF ) ) {
return NULL ;
}
2013-07-19 22:47:10 +00:00
if ( ast_channel_has_audio_frame_or_monitor ( chan ) ) {
2013-07-19 17:55:49 +00:00
/* Channel has an active monitor, audiohook, or framehook. */
return NULL ;
}
2013-05-21 18:00:22 +00:00
bridge_channel = ast_channel_internal_bridge_channel ( chan ) ;
if ( ! bridge_channel | | ast_bridge_channel_trylock ( bridge_channel ) ) {
return NULL ;
}
bridge = bridge_channel - > bridge ;
2013-07-25 02:20:23 +00:00
if ( bridge_channel - > activity ! = BRIDGE_CHANNEL_THREAD_SIMPLE
| | bridge_channel - > state ! = BRIDGE_CHANNEL_STATE_WAIT
2013-05-21 18:00:22 +00:00
| | ast_bridge_trylock ( bridge ) ) {
ast_bridge_channel_unlock ( bridge_channel ) ;
return NULL ;
}
2013-07-25 02:20:23 +00:00
if ( ! bridge_channel_internal_allows_optimization ( bridge_channel ) | |
2013-05-28 14:45:31 +00:00
! bridge_allows_optimization ( bridge ) ) {
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
ast_bridge_channel_unlock ( bridge_channel ) ;
return NULL ;
}
return bridge ;
}
/*!
* \ internal
* \ brief Lock the unreal channel stack for peer and prequalify it .
* \ since 12.0 .0
*
* \ param peer Other unreal channel in the pair .
*
* \ retval bridge on success with bridge , bridge_channel , and peer locked .
* \ retval NULL if cannot do optimization now .
*/
static struct ast_bridge * optimize_lock_peer_stack ( struct ast_channel * peer )
{
struct ast_bridge * bridge ;
struct ast_bridge_channel * bridge_channel ;
if ( ast_channel_trylock ( peer ) ) {
return NULL ;
}
if ( ! AST_LIST_EMPTY ( ast_channel_readq ( peer ) ) ) {
ast_channel_unlock ( peer ) ;
return NULL ;
}
2013-08-08 12:38:06 +00:00
if ( ast_test_flag ( ast_channel_flags ( peer ) , AST_FLAG_EMULATE_DTMF ) ) {
ast_channel_unlock ( peer ) ;
return NULL ;
}
2013-07-19 22:47:10 +00:00
if ( ast_channel_has_audio_frame_or_monitor ( peer ) ) {
2013-07-19 17:55:49 +00:00
/* Peer has an active monitor, audiohook, or framehook. */
ast_channel_unlock ( peer ) ;
return NULL ;
}
2013-05-21 18:00:22 +00:00
bridge_channel = ast_channel_internal_bridge_channel ( peer ) ;
if ( ! bridge_channel | | ast_bridge_channel_trylock ( bridge_channel ) ) {
ast_channel_unlock ( peer ) ;
return NULL ;
}
bridge = bridge_channel - > bridge ;
2013-07-25 02:20:23 +00:00
if ( bridge_channel - > activity ! = BRIDGE_CHANNEL_THREAD_IDLE
| | bridge_channel - > state ! = BRIDGE_CHANNEL_STATE_WAIT
2013-05-21 18:00:22 +00:00
| | ast_bridge_trylock ( bridge ) ) {
ast_bridge_channel_unlock ( bridge_channel ) ;
ast_channel_unlock ( peer ) ;
return NULL ;
}
2013-05-28 14:45:31 +00:00
if ( ! bridge_allows_optimization ( bridge ) | |
2013-07-25 02:20:23 +00:00
! bridge_channel_internal_allows_optimization ( bridge_channel ) ) {
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
ast_bridge_channel_unlock ( bridge_channel ) ;
ast_channel_unlock ( peer ) ;
return NULL ;
}
return bridge ;
}
/*!
* \ internal
2013-05-28 14:45:31 +00:00
* \ brief Indicates allowability of a swap optimization
*/
enum bridge_allow_swap {
/*! Bridges cannot allow for a swap optimization to occur */
SWAP_PROHIBITED ,
/*! Bridge swap optimization can occur into the chan_bridge */
SWAP_TO_CHAN_BRIDGE ,
/*! Bridge swap optimization can occur into the peer_bridge */
SWAP_TO_PEER_BRIDGE ,
} ;
/*!
* \ internal
* \ brief Determine if two bridges allow for swap optimization to occur
2013-05-21 18:00:22 +00:00
*
2013-05-28 14:45:31 +00:00
* \ param chan_bridge First bridge being tested
* \ param peer_bridge Second bridge being tested
* \ return Allowability of swap optimization
2013-05-21 18:00:22 +00:00
*/
2013-05-28 14:45:31 +00:00
static enum bridge_allow_swap bridges_allow_swap_optimization ( struct ast_bridge * chan_bridge ,
struct ast_bridge * peer_bridge )
2013-05-21 18:00:22 +00:00
{
int chan_priority ;
2013-05-28 14:45:31 +00:00
int peer_priority ;
2013-05-21 18:00:22 +00:00
if ( ! ast_test_flag ( & chan_bridge - > feature_flags ,
2013-05-28 14:45:31 +00:00
AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM |
AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY )
2013-05-21 18:00:22 +00:00
& & ! ast_test_flag ( & peer_bridge - > feature_flags ,
2013-05-28 14:45:31 +00:00
AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM |
AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY ) ) {
2013-05-21 18:00:22 +00:00
/*
* Can swap either way . Swap to the higher priority merge
* bridge .
*/
chan_priority = chan_bridge - > v_table - > get_merge_priority ( chan_bridge ) ;
peer_priority = peer_bridge - > v_table - > get_merge_priority ( peer_bridge ) ;
if ( chan_bridge - > num_channels = = 2
& & chan_priority < = peer_priority ) {
2013-05-28 14:45:31 +00:00
return SWAP_TO_PEER_BRIDGE ;
2013-05-21 18:00:22 +00:00
} else if ( peer_bridge - > num_channels = = 2
& & peer_priority < = chan_priority ) {
2013-05-28 14:45:31 +00:00
return SWAP_TO_CHAN_BRIDGE ;
2013-05-21 18:00:22 +00:00
}
} else if ( chan_bridge - > num_channels = = 2
2013-05-28 14:45:31 +00:00
& & ! ast_test_flag ( & chan_bridge - > feature_flags , AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY )
2013-05-21 18:00:22 +00:00
& & ! ast_test_flag ( & peer_bridge - > feature_flags , AST_BRIDGE_FLAG_SWAP_INHIBIT_TO ) ) {
/* Can swap optimize only one way. */
2013-05-28 14:45:31 +00:00
return SWAP_TO_PEER_BRIDGE ;
2013-05-21 18:00:22 +00:00
} else if ( peer_bridge - > num_channels = = 2
2013-05-28 14:45:31 +00:00
& & ! ast_test_flag ( & peer_bridge - > feature_flags , AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY )
2013-05-21 18:00:22 +00:00
& & ! ast_test_flag ( & chan_bridge - > feature_flags , AST_BRIDGE_FLAG_SWAP_INHIBIT_TO ) ) {
/* Can swap optimize only one way. */
2013-05-28 14:45:31 +00:00
return SWAP_TO_CHAN_BRIDGE ;
}
return SWAP_PROHIBITED ;
}
/*!
* \ internal
* \ brief Check and attempt to swap optimize out the unreal channels .
* \ since 12.0 .0
*
* \ param chan_bridge
* \ param chan_bridge_channel
* \ param peer_bridge
* \ param peer_bridge_channel
2013-07-08 14:26:40 +00:00
* \ param pvt Unreal data containing callbacks to call if the optimization actually
* happens
2013-05-28 14:45:31 +00:00
*
* \ retval 1 if unreal channels failed to optimize out .
* \ retval 0 if unreal channels were not optimized out .
* \ retval - 1 if unreal channels were optimized out .
*/
2013-07-08 14:26:40 +00:00
static int try_swap_optimize_out ( struct ast_bridge * chan_bridge ,
2013-05-28 14:45:31 +00:00
struct ast_bridge_channel * chan_bridge_channel , struct ast_bridge * peer_bridge ,
2013-07-08 14:26:40 +00:00
struct ast_bridge_channel * peer_bridge_channel ,
struct ast_unreal_pvt * pvt )
2013-05-28 14:45:31 +00:00
{
struct ast_bridge * dst_bridge ;
struct ast_bridge_channel * dst_bridge_channel ;
struct ast_bridge_channel * src_bridge_channel ;
struct ast_bridge_channel * other ;
int res = 1 ;
switch ( bridges_allow_swap_optimization ( chan_bridge , peer_bridge ) ) {
case SWAP_TO_CHAN_BRIDGE :
2013-05-21 18:00:22 +00:00
dst_bridge = chan_bridge ;
dst_bridge_channel = chan_bridge_channel ;
src_bridge_channel = peer_bridge_channel ;
2013-05-28 14:45:31 +00:00
break ;
case SWAP_TO_PEER_BRIDGE :
dst_bridge = peer_bridge ;
dst_bridge_channel = peer_bridge_channel ;
src_bridge_channel = chan_bridge_channel ;
break ;
case SWAP_PROHIBITED :
default :
return 0 ;
2013-05-21 18:00:22 +00:00
}
2013-05-28 14:45:31 +00:00
other = ast_bridge_channel_peer ( src_bridge_channel ) ;
2013-07-25 02:20:23 +00:00
if ( other & & other - > state = = BRIDGE_CHANNEL_STATE_WAIT ) {
2013-12-18 20:33:37 +00:00
unsigned int id ;
if ( ast_channel_trylock ( other - > chan ) ) {
return 1 ;
}
id = ast_atomic_fetchadd_int ( ( int * ) & optimization_id , + 1 ) ;
2013-08-22 18:52:41 +00:00
2014-05-28 22:54:12 +00:00
ast_verb ( 4 , " Move-swap optimizing %s <-- %s. \n " ,
2013-05-28 14:45:31 +00:00
ast_channel_name ( dst_bridge_channel - > chan ) ,
ast_channel_name ( other - > chan ) ) ;
2013-05-21 18:00:22 +00:00
2013-07-08 14:26:40 +00:00
if ( pvt & & ! ast_test_flag ( pvt , AST_UNREAL_OPTIMIZE_BEGUN ) & & pvt - > callbacks
& & pvt - > callbacks - > optimization_started ) {
2013-08-22 18:52:41 +00:00
pvt - > callbacks - > optimization_started ( pvt , other - > chan ,
dst_bridge_channel - > chan = = pvt - > owner ? AST_UNREAL_OWNER : AST_UNREAL_CHAN ,
id ) ;
2013-07-08 14:26:40 +00:00
ast_set_flag ( pvt , AST_UNREAL_OPTIMIZE_BEGUN ) ;
}
2013-05-28 14:45:31 +00:00
other - > swap = dst_bridge_channel - > chan ;
2013-07-24 15:38:18 +00:00
if ( ! bridge_do_move ( dst_bridge , other , 1 , 1 ) ) {
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( src_bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , AST_CAUSE_NORMAL_CLEARING ) ;
2013-05-28 14:45:31 +00:00
res = - 1 ;
2013-08-22 18:52:41 +00:00
}
if ( pvt & & pvt - > callbacks & & pvt - > callbacks - > optimization_finished ) {
pvt - > callbacks - > optimization_finished ( pvt , res = = 1 , id ) ;
2013-05-21 18:00:22 +00:00
}
2013-12-18 20:33:37 +00:00
ast_channel_unlock ( other - > chan ) ;
2013-05-21 18:00:22 +00:00
}
return res ;
}
2013-05-28 14:45:31 +00:00
/*!
* \ internal
* \ brief Indicates allowability of a merge optimization
*/
enum bridge_allow_merge {
/*! Bridge properties prohibit merge optimization */
MERGE_PROHIBITED ,
/*! Merge optimization cannot occur because the source bridge has too few channels */
MERGE_NOT_ENOUGH_CHANNELS ,
/*! Merge optimization cannot occur because multimix capability could not be requested */
MERGE_NO_MULTIMIX ,
/*! Merge optimization allowed between bridges */
MERGE_ALLOWED ,
} ;
/*!
* \ internal
* \ brief Determines allowability of a merge optimization
*
* \ note The merge output parameter is undefined if MERGE_PROHIBITED is returned . For success
* and other failure returns , a merge direction was determined , and the parameter is safe to
* access .
*
* \ param chan_bridge First bridge being tested
* \ param peer_bridge Second bridge being tested
* \ param num_kick_channels The number of channels to remove from the bridges during merging
* \ param [ out ] merge Indicates the recommended direction for the bridge merge
*/
static enum bridge_allow_merge bridges_allow_merge_optimization ( struct ast_bridge * chan_bridge ,
struct ast_bridge * peer_bridge , int num_kick_channels , struct merge_direction * merge )
{
* merge = bridge_merge_determine_direction ( chan_bridge , peer_bridge ) ;
if ( ! merge - > dest ) {
return MERGE_PROHIBITED ;
}
if ( merge - > src - > num_channels < 2 ) {
return MERGE_NOT_ENOUGH_CHANNELS ;
} else if ( ( 2 + num_kick_channels ) < merge - > dest - > num_channels + merge - > src - > num_channels
& & ! ( merge - > dest - > technology - > capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX )
& & ( ! ast_test_flag ( & merge - > dest - > feature_flags , AST_BRIDGE_FLAG_SMART )
| | ! ( merge - > dest - > allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX ) ) ) {
return MERGE_NO_MULTIMIX ;
}
return MERGE_ALLOWED ;
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Check and attempt to merge optimize out the unreal channels .
* \ since 12.0 .0
*
* \ param chan_bridge
* \ param chan_bridge_channel
* \ param peer_bridge
* \ param peer_bridge_channel
2013-07-08 14:26:40 +00:00
* \ param pvt Unreal data containing callbacks to call if the optimization actually
* happens
2013-05-21 18:00:22 +00:00
*
* \ retval 0 if unreal channels were not optimized out .
* \ retval - 1 if unreal channels were optimized out .
*/
2013-07-08 14:26:40 +00:00
static int try_merge_optimize_out ( struct ast_bridge * chan_bridge ,
2013-05-21 18:00:22 +00:00
struct ast_bridge_channel * chan_bridge_channel , struct ast_bridge * peer_bridge ,
2013-07-08 14:26:40 +00:00
struct ast_bridge_channel * peer_bridge_channel ,
struct ast_unreal_pvt * pvt )
2013-05-21 18:00:22 +00:00
{
struct merge_direction merge ;
2013-05-28 14:45:31 +00:00
struct ast_bridge_channel * kick_me [ ] = {
chan_bridge_channel ,
peer_bridge_channel ,
} ;
2013-08-22 18:52:41 +00:00
unsigned int id ;
2013-05-21 18:00:22 +00:00
2013-05-28 14:45:31 +00:00
switch ( bridges_allow_merge_optimization ( chan_bridge , peer_bridge , ARRAY_LEN ( kick_me ) , & merge ) ) {
case MERGE_ALLOWED :
break ;
case MERGE_PROHIBITED :
return 0 ;
case MERGE_NOT_ENOUGH_CHANNELS :
2013-05-21 18:00:22 +00:00
ast_debug ( 4 , " Can't optimize %s -- %s out, not enough channels in bridge %s. \n " ,
ast_channel_name ( chan_bridge_channel - > chan ) ,
ast_channel_name ( peer_bridge_channel - > chan ) ,
merge . src - > uniqueid ) ;
2013-05-28 14:45:31 +00:00
return 0 ;
case MERGE_NO_MULTIMIX :
2013-05-21 18:00:22 +00:00
ast_debug ( 4 , " Can't optimize %s -- %s out, multimix is needed and it cannot be acquired. \n " ,
ast_channel_name ( chan_bridge_channel - > chan ) ,
ast_channel_name ( peer_bridge_channel - > chan ) ) ;
2013-05-28 14:45:31 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
2014-05-28 22:54:12 +00:00
ast_verb ( 4 , " Merge optimizing %s -- %s out. \n " ,
2013-05-28 14:45:31 +00:00
ast_channel_name ( chan_bridge_channel - > chan ) ,
ast_channel_name ( peer_bridge_channel - > chan ) ) ;
2013-05-21 18:00:22 +00:00
2013-08-22 18:52:41 +00:00
id = ast_atomic_fetchadd_int ( ( int * ) & optimization_id , + 1 ) ;
2013-07-08 14:26:40 +00:00
if ( pvt & & ! ast_test_flag ( pvt , AST_UNREAL_OPTIMIZE_BEGUN ) & & pvt - > callbacks
& & pvt - > callbacks - > optimization_started ) {
2013-08-22 18:52:41 +00:00
pvt - > callbacks - > optimization_started ( pvt , NULL ,
merge . dest = = ast_channel_internal_bridge ( pvt - > owner ) ? AST_UNREAL_OWNER : AST_UNREAL_CHAN ,
id ) ;
2013-07-08 14:26:40 +00:00
ast_set_flag ( pvt , AST_UNREAL_OPTIMIZE_BEGUN ) ;
}
2013-07-24 15:38:18 +00:00
bridge_do_merge ( merge . dest , merge . src , kick_me , ARRAY_LEN ( kick_me ) , 1 ) ;
2013-07-08 14:26:40 +00:00
if ( pvt & & pvt - > callbacks & & pvt - > callbacks - > optimization_finished ) {
2013-08-22 18:52:41 +00:00
pvt - > callbacks - > optimization_finished ( pvt , 1 , id ) ;
2013-07-08 14:26:40 +00:00
}
2013-05-21 18:00:22 +00:00
2013-05-28 14:45:31 +00:00
return - 1 ;
2013-05-21 18:00:22 +00:00
}
2013-07-08 14:26:40 +00:00
int ast_bridge_unreal_optimize_out ( struct ast_channel * chan , struct ast_channel * peer , struct ast_unreal_pvt * pvt )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge * chan_bridge ;
struct ast_bridge * peer_bridge ;
struct ast_bridge_channel * chan_bridge_channel ;
struct ast_bridge_channel * peer_bridge_channel ;
int res = 0 ;
chan_bridge = optimize_lock_chan_stack ( chan ) ;
if ( ! chan_bridge ) {
return res ;
}
chan_bridge_channel = ast_channel_internal_bridge_channel ( chan ) ;
peer_bridge = optimize_lock_peer_stack ( peer ) ;
if ( peer_bridge ) {
peer_bridge_channel = ast_channel_internal_bridge_channel ( peer ) ;
2013-07-08 14:26:40 +00:00
res = try_swap_optimize_out ( chan_bridge , chan_bridge_channel ,
peer_bridge , peer_bridge_channel , pvt ) ;
2013-05-21 18:00:22 +00:00
if ( ! res ) {
2013-07-08 14:26:40 +00:00
res = try_merge_optimize_out ( chan_bridge , chan_bridge_channel ,
peer_bridge , peer_bridge_channel , pvt ) ;
2013-05-21 18:00:22 +00:00
} else if ( 0 < res ) {
res = 0 ;
}
/* Release peer locks. */
ast_bridge_unlock ( peer_bridge ) ;
ast_bridge_channel_unlock ( peer_bridge_channel ) ;
ast_channel_unlock ( peer ) ;
}
/* Release chan locks. */
ast_bridge_unlock ( chan_bridge ) ;
ast_bridge_channel_unlock ( chan_bridge_channel ) ;
return res ;
}
2013-05-28 14:45:31 +00:00
enum ast_bridge_optimization ast_bridges_allow_optimization ( struct ast_bridge * chan_bridge ,
struct ast_bridge * peer_bridge )
{
struct merge_direction merge ;
if ( ! bridge_allows_optimization ( chan_bridge ) | | ! bridge_allows_optimization ( peer_bridge ) ) {
return AST_BRIDGE_OPTIMIZE_PROHIBITED ;
}
switch ( bridges_allow_swap_optimization ( chan_bridge , peer_bridge ) ) {
case SWAP_TO_CHAN_BRIDGE :
return AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE ;
case SWAP_TO_PEER_BRIDGE :
return AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE ;
case SWAP_PROHIBITED :
default :
break ;
}
/* Two channels will be kicked from the bridges, the unreal;1 and unreal;2 channels */
if ( bridges_allow_merge_optimization ( chan_bridge , peer_bridge , 2 , & merge ) ! = MERGE_ALLOWED ) {
return AST_BRIDGE_OPTIMIZE_PROHIBITED ;
}
if ( merge . dest = = chan_bridge ) {
return AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE ;
} else {
return AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE ;
}
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Adjust the bridge merge inhibit request count .
* \ since 12.0 .0
*
* \ param bridge What to operate on .
* \ param request Inhibit request increment .
* ( Positive to add requests . Negative to remove requests . )
*
* \ note This function assumes bridge is locked .
*
* \ return Nothing
*/
2013-07-24 15:38:18 +00:00
void bridge_merge_inhibit_nolock ( struct ast_bridge * bridge , int request )
2013-05-21 18:00:22 +00:00
{
int new_request ;
new_request = bridge - > inhibit_merge + request ;
ast_assert ( 0 < = new_request ) ;
bridge - > inhibit_merge = new_request ;
}
void ast_bridge_merge_inhibit ( struct ast_bridge * bridge , int request )
{
ast_bridge_lock ( bridge ) ;
bridge_merge_inhibit_nolock ( bridge , request ) ;
ast_bridge_unlock ( bridge ) ;
}
int ast_bridge_suspend ( struct ast_bridge * bridge , struct ast_channel * chan )
{
struct ast_bridge_channel * bridge_channel ;
2013-08-15 18:20:52 +00:00
/* XXX ASTERISK-21271 the case of a disolved bridge while channel is suspended is not handled. */
/* XXX ASTERISK-21271 suspend/unsuspend needs to be rethought. The caller must block until it has successfully suspended the channel for temporary control. */
/* XXX ASTERISK-21271 external suspend/unsuspend needs to be eliminated. The channel may be playing a file at the time and stealing it then is not good. */
2013-05-21 18:00:22 +00:00
ast_bridge_lock ( bridge ) ;
2013-07-24 15:38:18 +00:00
if ( ! ( bridge_channel = bridge_find_channel ( bridge , chan ) ) ) {
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
return - 1 ;
}
2013-07-25 02:20:23 +00:00
bridge_channel_internal_suspend_nolock ( bridge_channel ) ;
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
return 0 ;
}
int ast_bridge_unsuspend ( struct ast_bridge * bridge , struct ast_channel * chan )
{
struct ast_bridge_channel * bridge_channel ;
2013-08-15 18:20:52 +00:00
/* XXX ASTERISK-21271 the case of a disolved bridge while channel is suspended is not handled. */
2013-05-21 18:00:22 +00:00
ast_bridge_lock ( bridge ) ;
2013-07-24 15:38:18 +00:00
if ( ! ( bridge_channel = bridge_find_channel ( bridge , chan ) ) ) {
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
return - 1 ;
}
2013-07-25 02:20:23 +00:00
bridge_channel_internal_unsuspend_nolock ( bridge_channel ) ;
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
return 0 ;
}
void ast_bridge_technology_suspend ( struct ast_bridge_technology * technology )
{
technology - > suspended = 1 ;
}
void ast_bridge_technology_unsuspend ( struct ast_bridge_technology * technology )
{
2013-08-15 17:57:33 +00:00
/*
* XXX We may want the act of unsuspending a bridge technology
* to prod all existing bridges to see if they should start
* using it .
*/
2013-05-21 18:00:22 +00:00
technology - > suspended = 0 ;
}
int ast_bridge_features_register ( enum ast_bridge_builtin_feature feature , ast_bridge_hook_callback callback , const char * dtmf )
{
if ( ARRAY_LEN ( builtin_features_handlers ) < = feature
| | builtin_features_handlers [ feature ] ) {
return - 1 ;
}
if ( ! ast_strlen_zero ( dtmf ) ) {
ast_copy_string ( builtin_features_dtmf [ feature ] , dtmf , sizeof ( builtin_features_dtmf [ feature ] ) ) ;
}
builtin_features_handlers [ feature ] = callback ;
return 0 ;
}
int ast_bridge_features_unregister ( enum ast_bridge_builtin_feature feature )
{
if ( ARRAY_LEN ( builtin_features_handlers ) < = feature
| | ! builtin_features_handlers [ feature ] ) {
return - 1 ;
}
builtin_features_handlers [ feature ] = NULL ;
return 0 ;
}
2013-07-26 21:34:23 +00:00
int ast_bridge_features_do ( enum ast_bridge_builtin_feature feature , struct ast_bridge_channel * bridge_channel , void * hook_pvt )
2013-07-05 14:54:27 +00:00
{
ast_bridge_hook_callback callback ;
if ( ARRAY_LEN ( builtin_features_handlers ) < = feature ) {
return - 1 ;
}
callback = builtin_features_handlers [ feature ] ;
if ( ! callback ) {
return - 1 ;
}
2013-07-26 21:34:23 +00:00
callback ( bridge_channel , hook_pvt ) ;
2013-07-05 14:54:27 +00:00
return 0 ;
}
2013-05-21 18:00:22 +00:00
int ast_bridge_interval_register ( enum ast_bridge_builtin_interval interval , ast_bridge_builtin_set_limits_fn callback )
{
if ( ARRAY_LEN ( builtin_interval_handlers ) < = interval
| | builtin_interval_handlers [ interval ] ) {
return - 1 ;
}
builtin_interval_handlers [ interval ] = callback ;
return 0 ;
}
int ast_bridge_interval_unregister ( enum ast_bridge_builtin_interval interval )
{
if ( ARRAY_LEN ( builtin_interval_handlers ) < = interval
| | ! builtin_interval_handlers [ interval ] ) {
return - 1 ;
}
builtin_interval_handlers [ interval ] = NULL ;
return 0 ;
}
/*!
* \ internal
* \ brief Bridge hook destructor .
* \ since 12.0 .0
*
* \ param vhook Object to destroy .
*
* \ return Nothing
*/
static void bridge_hook_destroy ( void * vhook )
{
struct ast_bridge_hook * hook = vhook ;
if ( hook - > destructor ) {
hook - > destructor ( hook - > hook_pvt ) ;
}
}
/*!
* \ internal
* \ brief Allocate and setup a generic bridge hook .
* \ since 12.0 .0
*
* \ param size How big an object to allocate .
* \ param callback Function to execute upon activation
* \ param hook_pvt Unique data
* \ param destructor Optional destructor callback for hook_pvt data
2013-06-05 19:19:48 +00:00
* \ param remove_flags Dictates what situations the hook should be removed .
2013-05-21 18:00:22 +00:00
*
* \ retval hook on success .
* \ retval NULL on error .
*/
static struct ast_bridge_hook * bridge_hook_generic ( size_t size ,
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
2013-06-05 19:19:48 +00:00
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_hook * hook ;
/* Allocate new hook and setup it's basic variables */
hook = ao2_alloc_options ( size , bridge_hook_destroy , AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( hook ) {
hook - > callback = callback ;
hook - > destructor = destructor ;
hook - > hook_pvt = hook_pvt ;
2013-06-05 19:19:48 +00:00
ast_set_flag ( & hook - > remove_flags , remove_flags ) ;
2013-05-21 18:00:22 +00:00
}
return hook ;
}
int ast_bridge_dtmf_hook ( struct ast_bridge_features * features ,
const char * dtmf ,
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
2013-06-05 19:19:48 +00:00
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
2013-07-24 21:13:00 +00:00
struct ast_bridge_hook_dtmf * hook ;
2013-05-21 18:00:22 +00:00
int res ;
/* Allocate new hook and setup it's various variables */
2013-07-24 21:13:00 +00:00
hook = ( struct ast_bridge_hook_dtmf * ) bridge_hook_generic ( sizeof ( * hook ) , callback ,
hook_pvt , destructor , remove_flags ) ;
2013-05-21 18:00:22 +00:00
if ( ! hook ) {
return - 1 ;
}
2013-07-24 21:13:00 +00:00
hook - > generic . type = AST_BRIDGE_HOOK_TYPE_DTMF ;
ast_copy_string ( hook - > dtmf . code , dtmf , sizeof ( hook - > dtmf . code ) ) ;
2013-05-21 18:00:22 +00:00
/* Once done we put it in the container. */
res = ao2_link ( features - > dtmf_hooks , hook ) ? 0 : - 1 ;
2013-06-20 17:21:40 +00:00
if ( res ) {
/*
* Could not link the hook into the container .
*
* Remove the hook_pvt destructor call from the hook since we
* are returning failure to install the hook .
*/
2013-07-24 21:13:00 +00:00
hook - > generic . destructor = NULL ;
2013-06-20 17:21:40 +00:00
}
2013-05-21 18:00:22 +00:00
ao2_ref ( hook , - 1 ) ;
return res ;
}
2013-07-24 21:13:00 +00:00
/*!
* \ internal
* \ brief Attach an other hook to a bridge features structure
*
* \ param features Bridge features structure
* \ param callback Function to execute upon activation
* \ param hook_pvt Unique data
* \ param destructor Optional destructor callback for hook_pvt data
* \ param remove_flags Dictates what situations the hook should be removed .
* \ param type What type of hook is being attached .
*
* \ retval 0 on success
* \ retval - 1 on failure ( The caller must cleanup any hook_pvt resources . )
*/
static int bridge_other_hook ( struct ast_bridge_features * features ,
2013-05-21 18:00:22 +00:00
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
2013-07-24 21:13:00 +00:00
enum ast_bridge_hook_remove_flags remove_flags ,
enum ast_bridge_hook_type type )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_hook * hook ;
int res ;
/* Allocate new hook and setup it's various variables */
hook = bridge_hook_generic ( sizeof ( * hook ) , callback , hook_pvt , destructor ,
2013-06-05 19:19:48 +00:00
remove_flags ) ;
2013-05-21 18:00:22 +00:00
if ( ! hook ) {
return - 1 ;
}
2013-07-24 21:13:00 +00:00
hook - > type = type ;
2013-05-21 18:00:22 +00:00
/* Once done we put it in the container. */
2013-07-24 21:13:00 +00:00
res = ao2_link ( features - > other_hooks , hook ) ? 0 : - 1 ;
2013-06-20 17:21:40 +00:00
if ( res ) {
/*
* Could not link the hook into the container .
*
* Remove the hook_pvt destructor call from the hook since we
* are returning failure to install the hook .
*/
hook - > destructor = NULL ;
}
2013-05-21 18:00:22 +00:00
ao2_ref ( hook , - 1 ) ;
return res ;
}
2013-07-24 21:13:00 +00:00
int ast_bridge_hangup_hook ( struct ast_bridge_features * features ,
2013-05-21 18:00:22 +00:00
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
2013-06-05 19:19:48 +00:00
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
2013-07-24 21:13:00 +00:00
return bridge_other_hook ( features , callback , hook_pvt , destructor , remove_flags ,
AST_BRIDGE_HOOK_TYPE_HANGUP ) ;
}
2013-05-21 18:00:22 +00:00
2013-07-24 21:13:00 +00:00
int ast_bridge_join_hook ( struct ast_bridge_features * features ,
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
enum ast_bridge_hook_remove_flags remove_flags )
{
return bridge_other_hook ( features , callback , hook_pvt , destructor , remove_flags ,
AST_BRIDGE_HOOK_TYPE_JOIN ) ;
2013-05-21 18:00:22 +00:00
}
int ast_bridge_leave_hook ( struct ast_bridge_features * features ,
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
2013-06-05 19:19:48 +00:00
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
2013-07-24 21:13:00 +00:00
return bridge_other_hook ( features , callback , hook_pvt , destructor , remove_flags ,
AST_BRIDGE_HOOK_TYPE_LEAVE ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-24 21:13:00 +00:00
int ast_bridge_talk_detector_hook ( struct ast_bridge_features * features ,
ast_bridge_talking_indicate_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
2013-07-24 21:13:00 +00:00
ast_bridge_hook_callback hook_cb = ( ast_bridge_hook_callback ) callback ;
return bridge_other_hook ( features , hook_cb , hook_pvt , destructor , remove_flags ,
AST_BRIDGE_HOOK_TYPE_TALK ) ;
2013-05-21 18:00:22 +00:00
}
2014-08-07 15:30:19 +00:00
int ast_bridge_move_hook ( struct ast_bridge_features * features ,
ast_bridge_move_indicate_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
enum ast_bridge_hook_remove_flags remove_flags )
{
ast_bridge_hook_callback hook_cb = ( ast_bridge_hook_callback ) callback ;
return bridge_other_hook ( features , hook_cb , hook_pvt , destructor , remove_flags ,
AST_BRIDGE_HOOK_TYPE_MOVE ) ;
}
2013-05-21 18:00:22 +00:00
int ast_bridge_interval_hook ( struct ast_bridge_features * features ,
2013-08-21 15:51:19 +00:00
enum ast_bridge_hook_timer_option flags ,
2013-05-21 18:00:22 +00:00
unsigned int interval ,
ast_bridge_hook_callback callback ,
void * hook_pvt ,
ast_bridge_hook_pvt_destructor destructor ,
2013-06-05 19:19:48 +00:00
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
2013-07-24 21:13:00 +00:00
struct ast_bridge_hook_timer * hook ;
2013-05-21 18:00:22 +00:00
int res ;
if ( ! features | | ! interval | | ! callback ) {
return - 1 ;
}
/* Allocate new hook and setup it's various variables */
2013-07-24 21:13:00 +00:00
hook = ( struct ast_bridge_hook_timer * ) bridge_hook_generic ( sizeof ( * hook ) , callback ,
hook_pvt , destructor , remove_flags ) ;
2013-05-21 18:00:22 +00:00
if ( ! hook ) {
return - 1 ;
}
2013-07-24 21:13:00 +00:00
hook - > generic . type = AST_BRIDGE_HOOK_TYPE_TIMER ;
hook - > timer . interval = interval ;
2013-08-21 15:51:19 +00:00
hook - > timer . trip_time = ast_tvadd ( ast_tvnow ( ) , ast_samp2tv ( interval , 1000 ) ) ;
2013-07-24 21:13:00 +00:00
hook - > timer . seqno = ast_atomic_fetchadd_int ( ( int * ) & features - > interval_sequence , + 1 ) ;
2013-08-21 15:51:19 +00:00
hook - > timer . flags = flags ;
2013-05-21 18:00:22 +00:00
ast_debug ( 1 , " Putting interval hook %p with interval %u in the heap on features %p \n " ,
2013-07-24 21:13:00 +00:00
hook , hook - > timer . interval , features ) ;
2013-05-21 18:00:22 +00:00
ast_heap_wrlock ( features - > interval_hooks ) ;
res = ast_heap_push ( features - > interval_hooks , hook ) ;
2013-06-20 17:21:40 +00:00
ast_heap_unlock ( features - > interval_hooks ) ;
2013-05-21 18:00:22 +00:00
if ( res ) {
2013-06-20 17:21:40 +00:00
/*
* Could not push the hook into the heap
*
* Remove the hook_pvt destructor call from the hook since we
* are returning failure to install the hook .
*/
2013-07-24 21:13:00 +00:00
hook - > generic . destructor = NULL ;
2013-05-21 18:00:22 +00:00
ao2_ref ( hook , - 1 ) ;
}
return res ? - 1 : 0 ;
}
int ast_bridge_features_enable ( struct ast_bridge_features * features ,
enum ast_bridge_builtin_feature feature ,
const char * dtmf ,
void * config ,
ast_bridge_hook_pvt_destructor destructor ,
2013-06-05 19:19:48 +00:00
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
if ( ARRAY_LEN ( builtin_features_handlers ) < = feature
| | ! builtin_features_handlers [ feature ] ) {
return - 1 ;
}
/* If no alternate DTMF stream was provided use the default one */
if ( ast_strlen_zero ( dtmf ) ) {
dtmf = builtin_features_dtmf [ feature ] ;
/* If no DTMF is still available (ie: it has been disabled) then error out now */
if ( ast_strlen_zero ( dtmf ) ) {
2014-05-09 22:49:26 +00:00
ast_debug ( 1 , " Failed to enable built in feature %u on %p, no DTMF string is available for it. \n " ,
2013-05-21 18:00:22 +00:00
feature , features ) ;
return - 1 ;
}
}
/*
* The rest is basically pretty easy . We create another hook
* using the built in feature ' s DTMF callback . Easy as pie .
*/
return ast_bridge_dtmf_hook ( features , dtmf , builtin_features_handlers [ feature ] ,
2013-06-05 19:19:48 +00:00
config , destructor , remove_flags ) ;
2013-05-21 18:00:22 +00:00
}
int ast_bridge_features_limits_construct ( struct ast_bridge_features_limits * limits )
{
memset ( limits , 0 , sizeof ( * limits ) ) ;
if ( ast_string_field_init ( limits , 256 ) ) {
return - 1 ;
}
return 0 ;
}
void ast_bridge_features_limits_destroy ( struct ast_bridge_features_limits * limits )
{
ast_string_field_free_memory ( limits ) ;
}
2013-06-05 19:19:48 +00:00
int ast_bridge_features_set_limits ( struct ast_bridge_features * features ,
2013-07-26 21:10:24 +00:00
struct ast_bridge_features_limits * limits ,
enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
if ( builtin_interval_handlers [ AST_BRIDGE_BUILTIN_INTERVAL_LIMITS ] ) {
2013-07-26 21:10:24 +00:00
ast_bridge_builtin_set_limits_fn callback ;
2013-05-21 18:00:22 +00:00
2013-07-26 21:10:24 +00:00
callback = builtin_interval_handlers [ AST_BRIDGE_BUILTIN_INTERVAL_LIMITS ] ;
return callback ( features , limits , remove_flags ) ;
2013-05-21 18:00:22 +00:00
}
ast_log ( LOG_ERROR , " Attempted to set limits without an AST_BRIDGE_BUILTIN_INTERVAL_LIMITS callback registered. \n " ) ;
return - 1 ;
}
void ast_bridge_features_set_flag ( struct ast_bridge_features * features , unsigned int flag )
{
ast_set_flag ( & features - > feature_flags , flag ) ;
features - > usable = 1 ;
}
/*!
* \ internal
2013-06-05 19:19:48 +00:00
* \ brief ao2 object match hooks with appropriate remove_flags .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
* \ param obj Feature hook object .
2013-06-05 18:07:23 +00:00
* \ param arg Removal flags
2013-06-06 20:47:10 +00:00
* \ param flags Not used
2013-05-21 18:00:22 +00:00
*
2013-06-05 18:07:23 +00:00
* \ retval CMP_MATCH if hook ' s remove_flags match the removal flags set .
2013-05-21 18:00:22 +00:00
* \ retval 0 if not match .
*/
2013-06-06 20:47:10 +00:00
static int hook_remove_match ( void * obj , void * arg , int flags )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_hook * hook = obj ;
2013-06-06 20:47:10 +00:00
enum ast_bridge_hook_remove_flags * remove_flags = arg ;
2013-05-21 18:00:22 +00:00
2013-06-06 20:47:10 +00:00
if ( ast_test_flag ( & hook - > remove_flags , * remove_flags ) ) {
2013-05-21 18:00:22 +00:00
return CMP_MATCH ;
} else {
return 0 ;
}
}
/*!
* \ internal
2013-06-05 19:19:48 +00:00
* \ brief Remove all hooks with appropriate remove_flags in the container .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
* \ param hooks Hooks container to work on .
2013-06-06 20:47:10 +00:00
* \ param remove_flags Determinator for whether hook is removed
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-06-06 20:47:10 +00:00
static void hooks_remove_container ( struct ao2_container * hooks , enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
ao2_callback ( hooks , OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE ,
2013-06-06 20:47:10 +00:00
hook_remove_match , & remove_flags ) ;
2013-05-21 18:00:22 +00:00
}
/*!
* \ internal
2013-06-05 19:19:48 +00:00
* \ brief Remove all hooks in the heap with appropriate remove_flags set .
2013-05-21 18:00:22 +00:00
* \ since 12.0 .0
*
* \ param hooks Hooks heap to work on .
2013-06-06 20:47:10 +00:00
* \ param remove_flags Determinator for whether hook is removed
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2013-06-06 20:47:10 +00:00
static void hooks_remove_heap ( struct ast_heap * hooks , enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
struct ast_bridge_hook * hook ;
int changed ;
ast_heap_wrlock ( hooks ) ;
do {
int idx ;
changed = 0 ;
for ( idx = ast_heap_size ( hooks ) ; idx ; - - idx ) {
hook = ast_heap_peek ( hooks , idx ) ;
2013-06-06 20:47:10 +00:00
if ( ast_test_flag ( & hook - > remove_flags , remove_flags ) ) {
2013-05-21 18:00:22 +00:00
ast_heap_remove ( hooks , hook ) ;
ao2_ref ( hook , - 1 ) ;
changed = 1 ;
}
}
} while ( changed ) ;
ast_heap_unlock ( hooks ) ;
}
2013-07-23 15:28:11 +00:00
void ast_bridge_features_remove ( struct ast_bridge_features * features , enum ast_bridge_hook_remove_flags remove_flags )
2013-05-21 18:00:22 +00:00
{
2013-06-06 20:47:10 +00:00
hooks_remove_container ( features - > dtmf_hooks , remove_flags ) ;
2013-07-24 21:13:00 +00:00
hooks_remove_container ( features - > other_hooks , remove_flags ) ;
2013-06-06 20:47:10 +00:00
hooks_remove_heap ( features - > interval_hooks , remove_flags ) ;
2013-05-21 18:00:22 +00:00
}
static int interval_hook_time_cmp ( void * a , void * b )
{
2013-07-24 21:13:00 +00:00
struct ast_bridge_hook_timer * hook_a = a ;
struct ast_bridge_hook_timer * hook_b = b ;
2013-05-21 18:00:22 +00:00
int cmp ;
2013-07-24 21:13:00 +00:00
cmp = ast_tvcmp ( hook_b - > timer . trip_time , hook_a - > timer . trip_time ) ;
2013-05-21 18:00:22 +00:00
if ( cmp ) {
return cmp ;
}
2013-07-24 21:13:00 +00:00
cmp = hook_b - > timer . seqno - hook_a - > timer . seqno ;
2013-05-21 18:00:22 +00:00
return cmp ;
}
/*!
* \ internal
* \ brief DTMF hook container sort comparison function .
* \ since 12.0 .0
*
* \ param obj_left pointer to the ( user - defined part ) of an object .
* \ param obj_right pointer to the ( user - defined part ) of an object .
* \ param flags flags from ao2_callback ( )
* OBJ_POINTER - if set , ' obj_right ' , is an object .
* OBJ_KEY - if set , ' obj_right ' , is a search key item that is not an object .
* OBJ_PARTIAL_KEY - if set , ' obj_right ' , is a partial search key item that is not an object .
*
* \ retval < 0 if obj_left < obj_right
* \ retval = 0 if obj_left = = obj_right
* \ retval > 0 if obj_left > obj_right
*/
static int bridge_dtmf_hook_sort ( const void * obj_left , const void * obj_right , int flags )
{
2013-07-24 21:13:00 +00:00
const struct ast_bridge_hook_dtmf * hook_left = obj_left ;
const struct ast_bridge_hook_dtmf * hook_right = obj_right ;
2013-05-21 18:00:22 +00:00
const char * right_key = obj_right ;
int cmp ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
default :
case OBJ_POINTER :
2013-07-24 21:13:00 +00:00
right_key = hook_right - > dtmf . code ;
2013-05-21 18:00:22 +00:00
/* Fall through */
case OBJ_KEY :
2013-07-24 21:13:00 +00:00
cmp = strcasecmp ( hook_left - > dtmf . code , right_key ) ;
2013-05-21 18:00:22 +00:00
break ;
case OBJ_PARTIAL_KEY :
2013-07-24 21:13:00 +00:00
cmp = strncasecmp ( hook_left - > dtmf . code , right_key , strlen ( right_key ) ) ;
2013-05-21 18:00:22 +00:00
break ;
}
return cmp ;
}
2014-06-26 12:43:47 +00:00
/*! \brief Callback for merging hook ao2_containers */
static int merge_container_cb ( void * obj , void * data , int flags )
{
ao2_link ( data , obj ) ;
return 0 ;
}
/*! \brief Wrapper for interval hooks that calls into the wrapped hook */
static int interval_wrapper_cb ( struct ast_bridge_channel * bridge_channel , void * obj )
{
struct ast_bridge_hook_timer * hook = obj ;
return hook - > generic . callback ( bridge_channel , hook - > generic . hook_pvt ) ;
}
/*! \brief Destructor for the hook wrapper */
static void interval_wrapper_pvt_dtor ( void * obj )
{
ao2_cleanup ( obj ) ;
}
/*! \brief Wrap the provided interval hook and add it to features */
static void wrap_hook ( struct ast_bridge_features * features , struct ast_bridge_hook_timer * hook )
{
/* Break out of the current wrapper if it exists to avoid multiple layers */
if ( hook - > generic . callback = = interval_wrapper_cb ) {
hook = hook - > generic . hook_pvt ;
}
ast_bridge_interval_hook ( features , hook - > timer . flags , hook - > timer . interval ,
interval_wrapper_cb , ao2_bump ( hook ) , interval_wrapper_pvt_dtor ,
hook - > generic . remove_flags . flags ) ;
}
void ast_bridge_features_merge ( struct ast_bridge_features * into , const struct ast_bridge_features * from )
{
struct ast_bridge_hook_timer * hook ;
int idx ;
/* Merge hook containers */
ao2_callback ( from - > dtmf_hooks , 0 , merge_container_cb , into - > dtmf_hooks ) ;
ao2_callback ( from - > other_hooks , 0 , merge_container_cb , into - > other_hooks ) ;
/* Merge hook heaps */
ast_heap_wrlock ( from - > interval_hooks ) ;
for ( idx = 1 ; ( hook = ast_heap_peek ( from - > interval_hooks , idx ) ) ; idx + + ) {
wrap_hook ( into , hook ) ;
}
ast_heap_unlock ( from - > interval_hooks ) ;
/* Merge feature flags */
into - > feature_flags . flags | = from - > feature_flags . flags ;
into - > usable | = from - > usable ;
into - > mute | = from - > mute ;
into - > dtmf_passthrough | = from - > dtmf_passthrough ;
}
2013-08-15 18:20:52 +00:00
/* XXX ASTERISK-21271 make ast_bridge_features_init() static when make ast_bridge_join() requires features to be allocated. */
2013-05-21 18:00:22 +00:00
int ast_bridge_features_init ( struct ast_bridge_features * features )
{
/* Zero out the structure */
memset ( features , 0 , sizeof ( * features ) ) ;
/* Initialize the DTMF hooks container */
features - > dtmf_hooks = ao2_container_alloc_list ( AO2_ALLOC_OPT_LOCK_MUTEX ,
AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE , bridge_dtmf_hook_sort , NULL ) ;
if ( ! features - > dtmf_hooks ) {
return - 1 ;
}
2013-07-24 21:13:00 +00:00
/* Initialize the miscellaneous other hooks container */
features - > other_hooks = ao2_container_alloc_list ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , NULL ,
2013-05-21 18:00:22 +00:00
NULL ) ;
2013-07-24 21:13:00 +00:00
if ( ! features - > other_hooks ) {
2013-05-21 18:00:22 +00:00
return - 1 ;
}
/* Initialize the interval hooks heap */
features - > interval_hooks = ast_heap_create ( 8 , interval_hook_time_cmp ,
2013-07-24 21:13:00 +00:00
offsetof ( struct ast_bridge_hook_timer , timer . heap_index ) ) ;
2013-05-21 18:00:22 +00:00
if ( ! features - > interval_hooks ) {
return - 1 ;
}
2013-08-15 12:17:41 +00:00
features - > dtmf_passthrough = 1 ;
2013-05-21 18:00:22 +00:00
return 0 ;
}
2013-08-15 18:20:52 +00:00
/* XXX ASTERISK-21271 make ast_bridge_features_cleanup() static when make ast_bridge_join() requires features to be allocated. */
2013-05-21 18:00:22 +00:00
void ast_bridge_features_cleanup ( struct ast_bridge_features * features )
{
2013-07-24 21:13:00 +00:00
struct ast_bridge_hook_timer * hook ;
2013-05-21 18:00:22 +00:00
/* Destroy the interval hooks heap. */
if ( features - > interval_hooks ) {
while ( ( hook = ast_heap_pop ( features - > interval_hooks ) ) ) {
ao2_ref ( hook , - 1 ) ;
}
features - > interval_hooks = ast_heap_destroy ( features - > interval_hooks ) ;
}
2013-07-24 21:13:00 +00:00
/* Destroy the miscellaneous other hooks container. */
ao2_cleanup ( features - > other_hooks ) ;
features - > other_hooks = NULL ;
2013-05-21 18:00:22 +00:00
/* Destroy the DTMF hooks container. */
ao2_cleanup ( features - > dtmf_hooks ) ;
features - > dtmf_hooks = NULL ;
}
void ast_bridge_features_destroy ( struct ast_bridge_features * features )
{
if ( ! features ) {
return ;
}
ast_bridge_features_cleanup ( features ) ;
ast_free ( features ) ;
}
struct ast_bridge_features * ast_bridge_features_new ( void )
{
struct ast_bridge_features * features ;
features = ast_malloc ( sizeof ( * features ) ) ;
if ( features ) {
if ( ast_bridge_features_init ( features ) ) {
ast_bridge_features_destroy ( features ) ;
features = NULL ;
}
}
return features ;
}
void ast_bridge_set_mixing_interval ( struct ast_bridge * bridge , unsigned int mixing_interval )
{
ast_bridge_lock ( bridge ) ;
2013-07-23 19:14:44 +00:00
bridge - > softmix . internal_mixing_interval = mixing_interval ;
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
}
2016-08-12 16:23:18 +00:00
void ast_bridge_set_binaural_active ( struct ast_bridge * bridge , unsigned int binaural_active )
{
ast_bridge_lock ( bridge ) ;
bridge - > softmix . binaural_active = binaural_active ;
ast_bridge_unlock ( bridge ) ;
}
2013-05-21 18:00:22 +00:00
void ast_bridge_set_internal_sample_rate ( struct ast_bridge * bridge , unsigned int sample_rate )
{
ast_bridge_lock ( bridge ) ;
2013-07-23 19:14:44 +00:00
bridge - > softmix . internal_sample_rate = sample_rate ;
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
}
static void cleanup_video_mode ( struct ast_bridge * bridge )
{
2013-07-23 19:14:44 +00:00
switch ( bridge - > softmix . video_mode . mode ) {
2013-05-21 18:00:22 +00:00
case AST_BRIDGE_VIDEO_MODE_NONE :
break ;
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc ) {
ast_channel_unref ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc ) ;
2013-05-21 18:00:22 +00:00
}
break ;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc ) {
ast_channel_unref ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc ) {
ast_channel_unref ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc ) ;
2013-05-21 18:00:22 +00:00
}
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
case AST_BRIDGE_VIDEO_MODE_SFU :
break ;
2013-05-21 18:00:22 +00:00
}
2013-07-23 19:14:44 +00:00
memset ( & bridge - > softmix . video_mode , 0 , sizeof ( bridge - > softmix . video_mode ) ) ;
2013-05-21 18:00:22 +00:00
}
void ast_bridge_set_single_src_video_mode ( struct ast_bridge * bridge , struct ast_channel * video_src_chan )
{
ast_bridge_lock ( bridge ) ;
cleanup_video_mode ( bridge ) ;
2013-07-23 19:14:44 +00:00
bridge - > softmix . video_mode . mode = AST_BRIDGE_VIDEO_MODE_SINGLE_SRC ;
bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc = ast_channel_ref ( video_src_chan ) ;
2016-11-04 20:36:42 +00:00
ast_verb ( 5 , " Video source in bridge '%s' (%s) is now '%s' (%s) \n " ,
bridge - > name , bridge - > uniqueid ,
ast_channel_name ( video_src_chan ) ,
ast_channel_uniqueid ( video_src_chan ) ) ;
2016-11-08 16:11:41 +00:00
ast_bridge_publish_state ( bridge ) ;
2013-05-21 18:00:22 +00:00
ast_indicate ( video_src_chan , AST_CONTROL_VIDUPDATE ) ;
ast_bridge_unlock ( bridge ) ;
}
void ast_bridge_set_talker_src_video_mode ( struct ast_bridge * bridge )
{
ast_bridge_lock ( bridge ) ;
cleanup_video_mode ( bridge ) ;
2013-07-23 19:14:44 +00:00
bridge - > softmix . video_mode . mode = AST_BRIDGE_VIDEO_MODE_TALKER_SRC ;
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
}
2017-05-24 15:09:22 +00:00
void ast_bridge_set_sfu_video_mode ( struct ast_bridge * bridge )
{
ast_bridge_lock ( bridge ) ;
cleanup_video_mode ( bridge ) ;
bridge - > softmix . video_mode . mode = AST_BRIDGE_VIDEO_MODE_SFU ;
ast_bridge_unlock ( bridge ) ;
}
2017-07-17 16:01:24 +00:00
void ast_bridge_set_video_update_discard ( struct ast_bridge * bridge , unsigned int video_update_discard )
{
ast_bridge_lock ( bridge ) ;
bridge - > softmix . video_mode . video_update_discard = video_update_discard ;
ast_bridge_unlock ( bridge ) ;
}
2013-05-21 18:00:22 +00:00
void ast_bridge_update_talker_src_video_mode ( struct ast_bridge * bridge , struct ast_channel * chan , int talker_energy , int is_keyframe )
{
struct ast_bridge_video_talker_src_data * data ;
2013-07-23 19:14:44 +00:00
2013-05-21 18:00:22 +00:00
/* If the channel doesn't support video, we don't care about it */
media formats: re-architect handling of media for performance improvements
In the old times media formats were represented using a bit field. This was
fast but had a few limitations.
1. Asterisk was limited in how many formats it could handle.
2. Formats, being a bit field, could not include any attribute information.
A format was strictly its type, e.g., "this is ulaw".
This was changed in Asterisk 10 (see
https://wiki.asterisk.org/wiki/display/AST/Media+Architecture+Proposal for
notes on that work) which led to the creation of the ast_format structure.
This structure allowed Asterisk to handle attributes and bundle information
with a format.
Additionally, ast_format_cap was created to act as a container for multiple
formats that, together, formed the capability of some entity. Another
mechanism was added to allow logic to be registered which performed format
attribute negotiation. Everywhere throughout the codebase Asterisk was
changed to use this strategy.
Unfortunately, in software, there is no free lunch. These new capabilities
came at a cost.
Performance analysis and profiling showed that we spend an inordinate
amount of time comparing, copying, and generally manipulating formats and
their related structures. Basic prototyping has shown that a reasonably
large performance improvement could be made in this area. This patch is the
result of that project, which overhauled the media format architecture
and its usage in Asterisk to improve performance.
Generally, the new philosophy for handling formats is as follows:
* The ast_format structure is reference counted. This removed a large amount
of the memory allocations and copying that was done in prior versions.
* In order to prevent race conditions while keeping things performant, the
ast_format structure is immutable by convention and lock-free. Violate this
tenet at your peril!
* Because formats are reference counted, codecs are also reference counted.
The Asterisk core generally provides built-in codecs and caches the
ast_format structures created to represent them. Generally, to prevent
inordinate amounts of module reference bumping, codecs and formats can be
added at run-time but cannot be removed.
* All compatibility with the bit field representation of codecs/formats has
been moved to a compatibility API. The primary user of this representation
is chan_iax2, which must continue to maintain its bit-field usage of formats
for interoperability concerns.
* When a format is negotiated with attributes, or when a format cannot be
represented by one of the cached formats, a new format object is created or
cloned from an existing format. That format may have the same codec
underlying it, but is a different format than a version of the format with
different attributes or without attributes.
* While formats are reference counted objects, the reference count maintained
on the format should be manipulated with care. Formats are generally cached
and will persist for the lifetime of Asterisk and do not explicitly need
to have their lifetime modified. An exception to this is when the user of a
format does not know where the format came from *and* the user may outlive
the provider of the format. This occurs, for example, when a format is read
from a channel: the channel may have a format with attributes (hence,
non-cached) and the user of the format may last longer than the channel (if
the reference to the channel is released prior to the format's reference).
For more information on this work, see the API design notes:
https://wiki.asterisk.org/wiki/display/AST/Media+Format+Rewrite
Finally, this work was the culmination of a large number of developer's
efforts. Extra thanks goes to Corey Farrell, who took on a large amount of the
work in the Asterisk core, chan_sip, and was an invaluable resource in peer
reviews throughout this project.
There were a substantial number of patches contributed during this work; the
following issues/patch names simply reflect some of the work (and will cause
the release scripts to give attribution to the individuals who work on them).
Reviews:
https://reviewboard.asterisk.org/r/3814
https://reviewboard.asterisk.org/r/3808
https://reviewboard.asterisk.org/r/3805
https://reviewboard.asterisk.org/r/3803
https://reviewboard.asterisk.org/r/3801
https://reviewboard.asterisk.org/r/3798
https://reviewboard.asterisk.org/r/3800
https://reviewboard.asterisk.org/r/3794
https://reviewboard.asterisk.org/r/3793
https://reviewboard.asterisk.org/r/3792
https://reviewboard.asterisk.org/r/3791
https://reviewboard.asterisk.org/r/3790
https://reviewboard.asterisk.org/r/3789
https://reviewboard.asterisk.org/r/3788
https://reviewboard.asterisk.org/r/3787
https://reviewboard.asterisk.org/r/3786
https://reviewboard.asterisk.org/r/3784
https://reviewboard.asterisk.org/r/3783
https://reviewboard.asterisk.org/r/3778
https://reviewboard.asterisk.org/r/3774
https://reviewboard.asterisk.org/r/3775
https://reviewboard.asterisk.org/r/3772
https://reviewboard.asterisk.org/r/3761
https://reviewboard.asterisk.org/r/3754
https://reviewboard.asterisk.org/r/3753
https://reviewboard.asterisk.org/r/3751
https://reviewboard.asterisk.org/r/3750
https://reviewboard.asterisk.org/r/3748
https://reviewboard.asterisk.org/r/3747
https://reviewboard.asterisk.org/r/3746
https://reviewboard.asterisk.org/r/3742
https://reviewboard.asterisk.org/r/3740
https://reviewboard.asterisk.org/r/3739
https://reviewboard.asterisk.org/r/3738
https://reviewboard.asterisk.org/r/3737
https://reviewboard.asterisk.org/r/3736
https://reviewboard.asterisk.org/r/3734
https://reviewboard.asterisk.org/r/3722
https://reviewboard.asterisk.org/r/3713
https://reviewboard.asterisk.org/r/3703
https://reviewboard.asterisk.org/r/3689
https://reviewboard.asterisk.org/r/3687
https://reviewboard.asterisk.org/r/3674
https://reviewboard.asterisk.org/r/3671
https://reviewboard.asterisk.org/r/3667
https://reviewboard.asterisk.org/r/3665
https://reviewboard.asterisk.org/r/3625
https://reviewboard.asterisk.org/r/3602
https://reviewboard.asterisk.org/r/3519
https://reviewboard.asterisk.org/r/3518
https://reviewboard.asterisk.org/r/3516
https://reviewboard.asterisk.org/r/3515
https://reviewboard.asterisk.org/r/3512
https://reviewboard.asterisk.org/r/3506
https://reviewboard.asterisk.org/r/3413
https://reviewboard.asterisk.org/r/3410
https://reviewboard.asterisk.org/r/3387
https://reviewboard.asterisk.org/r/3388
https://reviewboard.asterisk.org/r/3389
https://reviewboard.asterisk.org/r/3390
https://reviewboard.asterisk.org/r/3321
https://reviewboard.asterisk.org/r/3320
https://reviewboard.asterisk.org/r/3319
https://reviewboard.asterisk.org/r/3318
https://reviewboard.asterisk.org/r/3266
https://reviewboard.asterisk.org/r/3265
https://reviewboard.asterisk.org/r/3234
https://reviewboard.asterisk.org/r/3178
ASTERISK-23114 #close
Reported by: mjordan
media_formats_translation_core.diff uploaded by kharwell (License 6464)
rb3506.diff uploaded by mjordan (License 6283)
media_format_app_file.diff uploaded by kharwell (License 6464)
misc-2.diff uploaded by file (License 5000)
chan_mild-3.diff uploaded by file (License 5000)
chan_obscure.diff uploaded by file (License 5000)
jingle.diff uploaded by file (License 5000)
funcs.diff uploaded by file (License 5000)
formats.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
bridges.diff uploaded by file (License 5000)
mf-codecs-2.diff uploaded by file (License 5000)
mf-app_fax.diff uploaded by file (License 5000)
mf-apps-3.diff uploaded by file (License 5000)
media-formats-3.diff uploaded by file (License 5000)
ASTERISK-23715
rb3713.patch uploaded by coreyfarrell (License 5909)
rb3689.patch uploaded by mjordan (License 6283)
ASTERISK-23957
rb3722.patch uploaded by mjordan (License 6283)
mf-attributes-3.diff uploaded by file (License 5000)
ASTERISK-23958
Tested by: jrose
rb3822.patch uploaded by coreyfarrell (License 5909)
rb3800.patch uploaded by jrose (License 6182)
chan_sip.diff uploaded by mjordan (License 6283)
rb3747.patch uploaded by jrose (License 6182)
ASTERISK-23959 #close
Tested by: sgriepentrog, mjordan, coreyfarrell
sip_cleanup.diff uploaded by opticron (License 6273)
chan_sip_caps.diff uploaded by mjordan (License 6283)
rb3751.patch uploaded by coreyfarrell (License 5909)
chan_sip-3.diff uploaded by file (License 5000)
ASTERISK-23960 #close
Tested by: opticron
direct_media.diff uploaded by opticron (License 6273)
pjsip-direct-media.diff uploaded by file (License 5000)
format_cap_remove.diff uploaded by opticron (License 6273)
media_format_fixes.diff uploaded by opticron (License 6273)
chan_pjsip-2.diff uploaded by file (License 5000)
ASTERISK-23966 #close
Tested by: rmudgett
rb3803.patch uploaded by rmudgetti (License 5621)
chan_dahdi.diff uploaded by file (License 5000)
ASTERISK-24064 #close
Tested by: coreyfarrell, mjordan, opticron, file, rmudgett, sgriepentrog, jrose
rb3814.patch uploaded by rmudgett (License 5621)
moh_cleanup.diff uploaded by opticron (License 6273)
bridge_leak.diff uploaded by opticron (License 6273)
translate.diff uploaded by file (License 5000)
rb3795.patch uploaded by rmudgett (License 5621)
tls_fix.diff uploaded by mjordan (License 6283)
fax-mf-fix-2.diff uploaded by file (License 5000)
rtp_transfer_stuff uploaded by mjordan (License 6283)
rb3787.patch uploaded by rmudgett (License 5621)
media-formats-explicit-translate-format-3.diff uploaded by file (License 5000)
format_cache_case_fix.diff uploaded by opticron (License 6273)
rb3774.patch uploaded by rmudgett (License 5621)
rb3775.patch uploaded by rmudgett (License 5621)
rtp_engine_fix.diff uploaded by opticron (License 6273)
rtp_crash_fix.diff uploaded by opticron (License 6273)
rb3753.patch uploaded by mjordan (License 6283)
rb3750.patch uploaded by mjordan (License 6283)
rb3748.patch uploaded by rmudgett (License 5621)
media_format_fixes.diff uploaded by opticron (License 6273)
rb3740.patch uploaded by mjordan (License 6283)
rb3739.patch uploaded by mjordan (License 6283)
rb3734.patch uploaded by mjordan (License 6283)
rb3689.patch uploaded by mjordan (License 6283)
rb3674.patch uploaded by coreyfarrell (License 5909)
rb3671.patch uploaded by coreyfarrell (License 5909)
rb3667.patch uploaded by coreyfarrell (License 5909)
rb3665.patch uploaded by mjordan (License 6283)
rb3625.patch uploaded by coreyfarrell (License 5909)
rb3602.patch uploaded by coreyfarrell (License 5909)
format_compatibility-2.diff uploaded by file (License 5000)
core.diff uploaded by file (License 5000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419044 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-20 22:06:33 +00:00
if ( ! ast_format_cap_has_type ( ast_channel_nativeformats ( chan ) , AST_MEDIA_TYPE_VIDEO ) ) {
2013-05-21 18:00:22 +00:00
return ;
}
ast_bridge_lock ( bridge ) ;
2013-07-23 19:14:44 +00:00
data = & bridge - > softmix . video_mode . mode_data . talker_src_data ;
2013-05-21 18:00:22 +00:00
if ( data - > chan_vsrc = = chan ) {
data - > average_talking_energy = talker_energy ;
} else if ( ( data - > average_talking_energy < talker_energy ) & & is_keyframe ) {
if ( data - > chan_old_vsrc ) {
ast_channel_unref ( data - > chan_old_vsrc ) ;
}
if ( data - > chan_vsrc ) {
data - > chan_old_vsrc = data - > chan_vsrc ;
ast_indicate ( data - > chan_old_vsrc , AST_CONTROL_VIDUPDATE ) ;
}
data - > chan_vsrc = ast_channel_ref ( chan ) ;
data - > average_talking_energy = talker_energy ;
2016-11-04 20:36:42 +00:00
ast_verb ( 5 , " Video source in bridge '%s' (%s) is now '%s' (%s) \n " ,
bridge - > name , bridge - > uniqueid ,
ast_channel_name ( data - > chan_vsrc ) ,
ast_channel_uniqueid ( data - > chan_vsrc ) ) ;
2016-11-08 16:11:41 +00:00
ast_bridge_publish_state ( bridge ) ;
2013-05-21 18:00:22 +00:00
ast_indicate ( data - > chan_vsrc , AST_CONTROL_VIDUPDATE ) ;
} else if ( ( data - > average_talking_energy < talker_energy ) & & ! is_keyframe ) {
ast_indicate ( chan , AST_CONTROL_VIDUPDATE ) ;
} else if ( ! data - > chan_vsrc & & is_keyframe ) {
data - > chan_vsrc = ast_channel_ref ( chan ) ;
data - > average_talking_energy = talker_energy ;
2016-11-04 20:36:42 +00:00
ast_verb ( 5 , " Video source in bridge '%s' (%s) is now '%s' (%s) \n " ,
bridge - > name , bridge - > uniqueid ,
ast_channel_name ( data - > chan_vsrc ) ,
ast_channel_uniqueid ( data - > chan_vsrc ) ) ;
2016-11-08 16:11:41 +00:00
ast_bridge_publish_state ( bridge ) ;
2013-05-21 18:00:22 +00:00
ast_indicate ( chan , AST_CONTROL_VIDUPDATE ) ;
} else if ( ! data - > chan_old_vsrc & & is_keyframe ) {
data - > chan_old_vsrc = ast_channel_ref ( chan ) ;
ast_indicate ( chan , AST_CONTROL_VIDUPDATE ) ;
}
ast_bridge_unlock ( bridge ) ;
}
int ast_bridge_number_video_src ( struct ast_bridge * bridge )
{
int res = 0 ;
ast_bridge_lock ( bridge ) ;
2013-07-23 19:14:44 +00:00
switch ( bridge - > softmix . video_mode . mode ) {
2013-05-21 18:00:22 +00:00
case AST_BRIDGE_VIDEO_MODE_NONE :
break ;
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc ) {
2013-05-21 18:00:22 +00:00
res = 1 ;
}
break ;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc ) {
2013-05-21 18:00:22 +00:00
res + + ;
}
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc ) {
2013-05-21 18:00:22 +00:00
res + + ;
}
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
case AST_BRIDGE_VIDEO_MODE_SFU :
break ;
2013-05-21 18:00:22 +00:00
}
ast_bridge_unlock ( bridge ) ;
return res ;
}
int ast_bridge_is_video_src ( struct ast_bridge * bridge , struct ast_channel * chan )
{
int res = 0 ;
ast_bridge_lock ( bridge ) ;
2013-07-23 19:14:44 +00:00
switch ( bridge - > softmix . video_mode . mode ) {
2013-05-21 18:00:22 +00:00
case AST_BRIDGE_VIDEO_MODE_NONE :
break ;
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc = = chan ) {
2013-05-21 18:00:22 +00:00
res = 1 ;
}
break ;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc = = chan ) {
2013-05-21 18:00:22 +00:00
res = 1 ;
2013-07-23 19:14:44 +00:00
} else if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc = = chan ) {
2013-05-21 18:00:22 +00:00
res = 2 ;
}
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
case AST_BRIDGE_VIDEO_MODE_SFU :
break ;
2013-05-21 18:00:22 +00:00
}
ast_bridge_unlock ( bridge ) ;
return res ;
}
void ast_bridge_remove_video_src ( struct ast_bridge * bridge , struct ast_channel * chan )
{
ast_bridge_lock ( bridge ) ;
2013-07-23 19:14:44 +00:00
switch ( bridge - > softmix . video_mode . mode ) {
2013-05-21 18:00:22 +00:00
case AST_BRIDGE_VIDEO_MODE_NONE :
break ;
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc = = chan ) {
if ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc ) {
ast_channel_unref ( bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-23 19:14:44 +00:00
bridge - > softmix . video_mode . mode_data . single_src_data . chan_vsrc = NULL ;
2013-05-21 18:00:22 +00:00
}
break ;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC :
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc = = chan ) {
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc ) {
ast_channel_unref ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-23 19:14:44 +00:00
bridge - > softmix . video_mode . mode_data . talker_src_data . chan_vsrc = NULL ;
bridge - > softmix . video_mode . mode_data . talker_src_data . average_talking_energy = 0 ;
2013-05-21 18:00:22 +00:00
}
2013-07-23 19:14:44 +00:00
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc = = chan ) {
if ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc ) {
ast_channel_unref ( bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc ) ;
2013-05-21 18:00:22 +00:00
}
2013-07-23 19:14:44 +00:00
bridge - > softmix . video_mode . mode_data . talker_src_data . chan_old_vsrc = NULL ;
2013-05-21 18:00:22 +00:00
}
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
case AST_BRIDGE_VIDEO_MODE_SFU :
break ;
2012-06-26 21:45:22 +00:00
}
2013-05-21 18:00:22 +00:00
ast_bridge_unlock ( bridge ) ;
}
2012-06-26 21:45:22 +00:00
2016-11-08 16:11:41 +00:00
const char * ast_bridge_video_mode_to_string ( enum ast_bridge_video_mode_type video_mode )
{
switch ( video_mode ) {
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC :
return " talker " ;
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC :
return " single " ;
Add primitive SFU support to bridge_softmix.
This sets up the "plumbing" in bridge_softmix to
be able to accommodate Asterisk asking as an SFU
(selective forwarding unit) for conferences.
The way this works is that whenever a channel enters or leaves a
conference, all participants in the bridge get sent a stream topology
change request. The topologies consist of the channels' original
topology, along with video destination streams corresponding to each
participants' source video streams. So for instance, if Alice, Bob, and
Carol are in the conference, and each supplies one video stream, then
the topologies for each would look like so:
Alice:
Audio,
Source video(Alice),
Destination Video(Bob),
Destination video (Carol)
Bob:
Audio,
Source video(Bob)
Destination Video(Alice),
Destination video (Carol)
Carol:
Audio,
Source video(Carol)
Destination Video(Alice),
Destination video (Bob)
This way, video that arrives from a source video stream can then be
copied out to the destination video streams on the other participants'
channels.
Once the bridge gets told that a topology on a channel has changed, the
bridge constructs a map in order to get the video frames routed to the
proper destination streams. This is done using the bridge channel's
stream_map.
This change is bare-bones with regards to SFU support. Some key features
are missing at this point:
* Stream limits. This commit makes no effort to limit the number of
streams on a specific channel. This means that if there were 50 video
callers in a conference, bridge_softmix will happily send out topology
change requests to every channel in the bridge, requesting 50+
streams.
* Configuration. The plumbing has been added to bridge_softmix, but
there has been nothing added as of yet to app_confbridge to enable SFU
video mode.
* Testing. Some functions included here have unit tests.
However, the functionality as a whole has only been verified by
hand-tracing the code.
* Selectivenss. For a "selective" forwarding unit, this does not
currently have any means of being selective.
* Features. Presumably, someone might wish to only receive video from
specific sources. There are no external-facing functions at the moment
that allow for users to select who they receive video from.
* Efficiency. The current scheme treats all video streams as being
unidirectional. We could be re-using a source video stream as a
desetnation, too. But to simplify things on this first round, I did it
this way.
Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
case AST_BRIDGE_VIDEO_MODE_SFU :
return " sfu " ;
2016-11-08 16:11:41 +00:00
case AST_BRIDGE_VIDEO_MODE_NONE :
default :
return " none " ;
}
}
2013-05-21 18:00:22 +00:00
static int channel_hash ( const void * obj , int flags )
{
const struct ast_channel * chan = obj ;
const char * name = obj ;
int hash ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
default :
case OBJ_POINTER :
name = ast_channel_name ( chan ) ;
/* Fall through */
case OBJ_KEY :
hash = ast_str_hash ( name ) ;
break ;
case OBJ_PARTIAL_KEY :
/* Should never happen in hash callback. */
ast_assert ( 0 ) ;
hash = 0 ;
break ;
2011-04-21 18:11:40 +00:00
}
2013-05-21 18:00:22 +00:00
return hash ;
2011-04-21 18:11:40 +00:00
}
2013-05-21 18:00:22 +00:00
static int channel_cmp ( void * obj , void * arg , int flags )
2011-04-21 18:11:40 +00:00
{
2013-05-21 18:00:22 +00:00
const struct ast_channel * left = obj ;
const struct ast_channel * right = arg ;
const char * right_name = arg ;
int cmp ;
2013-01-21 18:45:17 +00:00
2013-05-21 18:00:22 +00:00
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
default :
case OBJ_POINTER :
right_name = ast_channel_name ( right ) ;
/* Fall through */
case OBJ_KEY :
cmp = strcmp ( ast_channel_name ( left ) , right_name ) ;
break ;
case OBJ_PARTIAL_KEY :
cmp = strncmp ( ast_channel_name ( left ) , right_name , strlen ( right_name ) ) ;
break ;
2011-04-21 18:11:40 +00:00
}
2013-05-21 18:00:22 +00:00
return cmp ? 0 : CMP_MATCH ;
2011-04-21 18:11:40 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
struct ao2_container * ast_bridge_peers_nolock ( struct ast_bridge * bridge )
2011-04-21 18:11:40 +00:00
{
2013-05-21 18:00:22 +00:00
struct ao2_container * channels ;
struct ast_bridge_channel * iter ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
channels = ao2_container_alloc_options ( AO2_ALLOC_OPT_LOCK_NOLOCK ,
13 , channel_hash , channel_cmp ) ;
if ( ! channels ) {
return NULL ;
2011-04-21 18:11:40 +00:00
}
2013-05-21 18:00:22 +00:00
AST_LIST_TRAVERSE ( & bridge - > channels , iter , entry ) {
ao2_link ( channels , iter - > chan ) ;
2011-04-21 18:11:40 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
return channels ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
struct ao2_container * ast_bridge_peers ( struct ast_bridge * bridge )
{
struct ao2_container * channels ;
2011-04-21 18:11:40 +00:00
2013-05-21 18:00:22 +00:00
ast_bridge_lock ( bridge ) ;
channels = ast_bridge_peers_nolock ( bridge ) ;
ast_bridge_unlock ( bridge ) ;
2011-04-21 18:11:40 +00:00
2013-05-21 18:00:22 +00:00
return channels ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
struct ast_channel * ast_bridge_peer_nolock ( struct ast_bridge * bridge , struct ast_channel * chan )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
struct ast_channel * peer = NULL ;
struct ast_bridge_channel * iter ;
/* Asking for the peer channel only makes sense on a two-party bridge. */
if ( bridge - > num_channels = = 2
& & bridge - > technology - > capabilities
& ( AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX ) ) {
int in_bridge = 0 ;
AST_LIST_TRAVERSE ( & bridge - > channels , iter , entry ) {
if ( iter - > chan ! = chan ) {
peer = iter - > chan ;
} else {
in_bridge = 1 ;
}
}
if ( in_bridge & & peer ) {
ast_channel_ref ( peer ) ;
} else {
peer = NULL ;
}
2012-06-26 21:45:22 +00:00
}
2013-05-21 18:00:22 +00:00
return peer ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
struct ast_channel * ast_bridge_peer ( struct ast_bridge * bridge , struct ast_channel * chan )
{
struct ast_channel * peer ;
2011-04-21 18:11:40 +00:00
2013-05-21 18:00:22 +00:00
ast_bridge_lock ( bridge ) ;
peer = ast_bridge_peer_nolock ( bridge , chan ) ;
ast_bridge_unlock ( bridge ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
return peer ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Transfer an entire bridge to a specific destination .
*
* This creates a local channel to dial out and swaps the called local channel
* with the transferer channel . By doing so , all participants in the bridge are
* connected to the specified destination .
*
* While this means of transferring would work for both two - party and multi - party
* bridges , this method is only used for multi - party bridges since this method would
* be less efficient for two - party bridges .
*
2014-08-20 13:06:33 +00:00
* \ param is_external Whether the transfer is externally initiated
2013-05-21 18:00:22 +00:00
* \ param transferer The channel performing a transfer
* \ param bridge The bridge where the transfer is being performed
* \ param exten The destination extension for the blind transfer
* \ param context The destination context for the blind transfer
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
* \ param transferee The party being transferred if there is only one
* \ param new_channel_cb Callback to call on channel that is created to
* facilitate the blind transfer .
* \ param user_data_wrapper User - provided data needed in new_channel_cb
* \ param transfer_message The Stasis publication for this transfer .
2014-08-20 13:06:33 +00:00
*
2013-05-21 18:00:22 +00:00
* \ return The success or failure of the operation
*/
2014-08-20 13:06:33 +00:00
static enum ast_transfer_result blind_transfer_bridge ( int is_external ,
struct ast_channel * transferer , struct ast_bridge * bridge ,
const char * exten , const char * context , struct ast_channel * transferee ,
transfer_channel_cb new_channel_cb ,
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
struct transfer_channel_data * user_data_wrapper ,
struct ast_blind_transfer_message * transfer_message )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
struct ast_channel * local ;
char chan_name [ AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2 ] ;
int cause ;
snprintf ( chan_name , sizeof ( chan_name ) , " %s@%s " , exten , context ) ;
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
local = ast_request ( " Local " , ast_channel_nativeformats ( transferer ) , NULL , transferer ,
2013-05-21 18:00:22 +00:00
chan_name , & cause ) ;
if ( ! local ) {
return AST_BRIDGE_TRANSFER_FAIL ;
}
2012-06-26 21:45:22 +00:00
accountcode: Slightly change accountcode propagation.
The previous behavior was to simply set the accountcode of an outgoing
channel to the accountcode of the channel initiating the call. It was
done this way a long time ago to allow the accountcode set on the SIP/100
channel to be propagated to a local channel so the dialplan execution on
the Local;2 channel would have the SIP/100 accountcode available.
SIP/100 -> Local;1/Local;2 -> SIP/200
Propagating the SIP/100 accountcode to the local channels is very useful.
Without any dialplan manipulation, all channels in this call would have
the same accountcode.
Using dialplan, you can set a different accountcode on the SIP/200 channel
either by setting the accountcode on the Local;2 channel or by the Dial
application's b(pre-dial), M(macro) or U(gosub) options, or by the
FollowMe application's b(pre-dial) option, or by the Queue application's
macro or gosub options. Before Asterisk v12, the altered accountcode on
SIP/200 will remain until the local channels optimize out and the
accountcode would change to the SIP/100 accountcode.
Asterisk v1.8 attempted to add peeraccount support but ultimately had to
punt on the support. The peeraccount support was rendered useless because
of how the CDR code needed to unconditionally force the caller's
accountcode onto the peer channel's accountcode. The CEL events were thus
intentionally made to always use the channel's accountcode as the
peeraccount value.
With the arrival of Asterisk v12, the situation has improved somewhat so
peeraccount support can be made to work. Using the indicated example, the
the accountcode values become as follows when the peeraccount is set on
SIP/100 before calling SIP/200:
SIP/100 ---> Local;1 ---- Local;2 ---> SIP/200
acct: 100 \/ acct: 200 \/ acct: 100 \/ acct: 200
peer: 200 /\ peer: 100 /\ peer: 200 /\ peer: 100
If a channel already has an accountcode it can only change by the
following explicit user actions:
1) A channel originate method that can specify an accountcode to use.
2) The calling channel propagating its non-empty peeraccount or its
non-empty accountcode if the peeraccount was empty to the outgoing
channel's accountcode before initiating the dial. e.g., Dial and
FollowMe. The exception to this propagation method is Queue. Queue will
only propagate peeraccounts this way only if the outgoing channel does not
have an accountcode.
3) Dialplan using CHANNEL(accountcode).
4) Dialplan using CHANNEL(peeraccount) on the other end of a local
channel pair.
If a channel does not have an accountcode it can get one from the
following places:
1) The channel driver's configuration at channel creation.
2) Explicit user action as already indicated.
3) Entering a basic or stasis-mixing bridge from a peer channel's
peeraccount value.
You can specify the accountcode for an outgoing channel by setting the
CHANNEL(peeraccount) before using the Dial, FollowMe, and Queue
applications. Queue adds the wrinkle that it will not overwrite an
existing accountcode on the outgoing channel with the calling channels
values.
Accountcode and peeraccount values propagate to an outgoing channel before
dialing. Accountcodes also propagate when channels enter or leave a basic
or stasis-mixing bridge. The peeraccount value only makes sense for
mixing bridges with two channels; it is meaningless otherwise.
* Made peeraccount functional by changing accountcode propagation as
described above.
* Fixed CEL extracting the wrong ie value for the peeraccount. This was
done intentionally in Asterisk v1.8 when that version had to punt on
peeraccount.
* Fixed a few places dealing with accountcodes that were reading from
channels without the lock held.
AFS-65 #close
Review: https://reviewboard.asterisk.org/r/3601/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419520 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-24 22:48:38 +00:00
ast_channel_lock_both ( local , transferer ) ;
ast_channel_req_accountcodes ( local , transferer , AST_CHANNEL_REQUESTOR_REPLACEMENT ) ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
transfer_message - > replace_channel = ast_channel_snapshot_get_latest ( ast_channel_uniqueid ( local ) ) ;
if ( ! transfer_message - > replace_channel ) {
ast_hangup ( local ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
2013-12-13 20:13:22 +00:00
pbx_builtin_setvar_helper ( local , BLINDTRANSFER , ast_channel_name ( transferer ) ) ;
accountcode: Slightly change accountcode propagation.
The previous behavior was to simply set the accountcode of an outgoing
channel to the accountcode of the channel initiating the call. It was
done this way a long time ago to allow the accountcode set on the SIP/100
channel to be propagated to a local channel so the dialplan execution on
the Local;2 channel would have the SIP/100 accountcode available.
SIP/100 -> Local;1/Local;2 -> SIP/200
Propagating the SIP/100 accountcode to the local channels is very useful.
Without any dialplan manipulation, all channels in this call would have
the same accountcode.
Using dialplan, you can set a different accountcode on the SIP/200 channel
either by setting the accountcode on the Local;2 channel or by the Dial
application's b(pre-dial), M(macro) or U(gosub) options, or by the
FollowMe application's b(pre-dial) option, or by the Queue application's
macro or gosub options. Before Asterisk v12, the altered accountcode on
SIP/200 will remain until the local channels optimize out and the
accountcode would change to the SIP/100 accountcode.
Asterisk v1.8 attempted to add peeraccount support but ultimately had to
punt on the support. The peeraccount support was rendered useless because
of how the CDR code needed to unconditionally force the caller's
accountcode onto the peer channel's accountcode. The CEL events were thus
intentionally made to always use the channel's accountcode as the
peeraccount value.
With the arrival of Asterisk v12, the situation has improved somewhat so
peeraccount support can be made to work. Using the indicated example, the
the accountcode values become as follows when the peeraccount is set on
SIP/100 before calling SIP/200:
SIP/100 ---> Local;1 ---- Local;2 ---> SIP/200
acct: 100 \/ acct: 200 \/ acct: 100 \/ acct: 200
peer: 200 /\ peer: 100 /\ peer: 200 /\ peer: 100
If a channel already has an accountcode it can only change by the
following explicit user actions:
1) A channel originate method that can specify an accountcode to use.
2) The calling channel propagating its non-empty peeraccount or its
non-empty accountcode if the peeraccount was empty to the outgoing
channel's accountcode before initiating the dial. e.g., Dial and
FollowMe. The exception to this propagation method is Queue. Queue will
only propagate peeraccounts this way only if the outgoing channel does not
have an accountcode.
3) Dialplan using CHANNEL(accountcode).
4) Dialplan using CHANNEL(peeraccount) on the other end of a local
channel pair.
If a channel does not have an accountcode it can get one from the
following places:
1) The channel driver's configuration at channel creation.
2) Explicit user action as already indicated.
3) Entering a basic or stasis-mixing bridge from a peer channel's
peeraccount value.
You can specify the accountcode for an outgoing channel by setting the
CHANNEL(peeraccount) before using the Dial, FollowMe, and Queue
applications. Queue adds the wrinkle that it will not overwrite an
existing accountcode on the outgoing channel with the calling channels
values.
Accountcode and peeraccount values propagate to an outgoing channel before
dialing. Accountcodes also propagate when channels enter or leave a basic
or stasis-mixing bridge. The peeraccount value only makes sense for
mixing bridges with two channels; it is meaningless otherwise.
* Made peeraccount functional by changing accountcode propagation as
described above.
* Fixed CEL extracting the wrong ie value for the peeraccount. This was
done intentionally in Asterisk v1.8 when that version had to punt on
peeraccount.
* Fixed a few places dealing with accountcodes that were reading from
channels without the lock held.
AFS-65 #close
Review: https://reviewboard.asterisk.org/r/3601/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419520 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-24 22:48:38 +00:00
ast_channel_unlock ( local ) ;
ast_channel_unlock ( transferer ) ;
2013-12-13 20:13:22 +00:00
2013-05-21 18:00:22 +00:00
if ( new_channel_cb ) {
2014-05-22 15:52:30 +00:00
new_channel_cb ( local , user_data_wrapper , AST_BRIDGE_TRANSFER_MULTI_PARTY ) ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
if ( ast_call ( local , chan_name , 0 ) ) {
ast_hangup ( local ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
2013-09-13 22:19:23 +00:00
if ( ast_bridge_impart ( bridge , local , transferer , NULL ,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT ) ) {
2013-05-21 18:00:22 +00:00
ast_hangup ( local ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
2013-05-21 18:00:22 +00:00
return AST_BRIDGE_TRANSFER_SUCCESS ;
}
2009-03-05 18:18:27 +00:00
2014-08-07 15:30:19 +00:00
/*!
* \ internal
* \ brief Get the transferee channel
*
* This is only applicable to cases where a transfer is occurring on a
* two - party bridge . The channels container passed in is expected to only
* contain two channels , the transferer and the transferee . The transferer
* channel is passed in as a parameter to ensure we don ' t return it as
* the transferee channel .
*
* \ param channels A two - channel container containing the transferer and transferee
* \ param transferer The party that is transfering the call
* \ return The party that is being transferred
*/
static struct ast_channel * get_transferee ( struct ao2_container * channels , struct ast_channel * transferer )
{
struct ao2_iterator channel_iter ;
struct ast_channel * transferee ;
for ( channel_iter = ao2_iterator_init ( channels , 0 ) ;
( transferee = ao2_iterator_next ( & channel_iter ) ) ;
ao2_cleanup ( transferee ) ) {
if ( transferee ! = transferer ) {
break ;
}
}
ao2_iterator_destroy ( & channel_iter ) ;
return transferee ;
}
2013-05-28 14:45:31 +00:00
/*!
* \ brief Perform an attended transfer of a bridge
*
* This performs an attended transfer of an entire bridge to a target .
* The target varies , depending on what bridges exist during the transfer
* attempt .
*
* If two bridges exist , then a local channel is created to link the two
2013-06-11 22:57:09 +00:00
* bridges together .
2013-05-28 14:45:31 +00:00
*
* If only one bridge exists , then a local channel is created with one end
* placed into the existing bridge and the other end masquerading into
* the unbridged channel .
*
* \ param chan1 Transferer channel . Guaranteed to be bridged .
* \ param chan2 Other transferer channel . May or may not be bridged .
* \ param bridge1 Bridge that chan1 is in . Guaranteed to be non - NULL .
* \ param bridge2 Bridge that chan2 is in . If NULL , then chan2 is not bridged .
2013-06-28 18:42:24 +00:00
* \ param publication Data to publish for a stasis attended transfer message .
2013-05-28 14:45:31 +00:00
* \ retval AST_BRIDGE_TRANSFER_FAIL Internal error occurred
* \ retval AST_BRIDGE_TRANSFER_SUCCESS Succesfully transferred the bridge
*/
static enum ast_transfer_result attended_transfer_bridge ( struct ast_channel * chan1 ,
2013-06-28 18:42:24 +00:00
struct ast_channel * chan2 , struct ast_bridge * bridge1 , struct ast_bridge * bridge2 ,
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
struct ast_attended_transfer_message * transfer_msg )
2013-05-28 14:45:31 +00:00
{
2015-07-08 19:56:10 +00:00
# define BRIDGE_LOCK_ONE_OR_BOTH(b1, b2) \
do { \
if ( b2 ) { \
ast_bridge_lock_both ( b1 , b2 ) ; \
} else { \
ast_bridge_lock ( b1 ) ; \
} \
} while ( 0 )
2013-05-28 14:45:31 +00:00
static const char * dest = " _attended@transfer/m " ;
struct ast_channel * local_chan ;
int cause ;
int res ;
2013-06-28 18:42:24 +00:00
const char * app = NULL ;
2013-05-28 14:45:31 +00:00
uniqueid: channel linkedid, ami, ari object creation with id's
Much needed was a way to assign id to objects on creation, and
much change was necessary to accomplish it. Channel uniqueids
and linkedids are split into separate string and creation time
components without breaking linkedid propgation. This allowed
the uniqueid to be specified by the user interface - and those
values are now carried through to channel creation, adding the
assignedids value to every function in the chain including the
channel drivers. For local channels, the second channel can be
specified or left to default to a ;2 suffix of first. In ARI,
bridge, playback, and snoop objects can also be created with a
specified uniqueid.
Along the way, the args order to allocating channels was fixed
in chan_mgcp and chan_gtalk, and linkedid is no longer lost as
masquerade occurs.
(closes issue ASTERISK-23120)
Review: https://reviewboard.asterisk.org/r/3191/
........
Merged revisions 410157 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@410158 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-03-07 15:47:55 +00:00
local_chan = ast_request ( " Local " , ast_channel_nativeformats ( chan1 ) , NULL , chan1 ,
2013-05-28 14:45:31 +00:00
dest , & cause ) ;
if ( ! local_chan ) {
return AST_BRIDGE_TRANSFER_FAIL ;
}
accountcode: Slightly change accountcode propagation.
The previous behavior was to simply set the accountcode of an outgoing
channel to the accountcode of the channel initiating the call. It was
done this way a long time ago to allow the accountcode set on the SIP/100
channel to be propagated to a local channel so the dialplan execution on
the Local;2 channel would have the SIP/100 accountcode available.
SIP/100 -> Local;1/Local;2 -> SIP/200
Propagating the SIP/100 accountcode to the local channels is very useful.
Without any dialplan manipulation, all channels in this call would have
the same accountcode.
Using dialplan, you can set a different accountcode on the SIP/200 channel
either by setting the accountcode on the Local;2 channel or by the Dial
application's b(pre-dial), M(macro) or U(gosub) options, or by the
FollowMe application's b(pre-dial) option, or by the Queue application's
macro or gosub options. Before Asterisk v12, the altered accountcode on
SIP/200 will remain until the local channels optimize out and the
accountcode would change to the SIP/100 accountcode.
Asterisk v1.8 attempted to add peeraccount support but ultimately had to
punt on the support. The peeraccount support was rendered useless because
of how the CDR code needed to unconditionally force the caller's
accountcode onto the peer channel's accountcode. The CEL events were thus
intentionally made to always use the channel's accountcode as the
peeraccount value.
With the arrival of Asterisk v12, the situation has improved somewhat so
peeraccount support can be made to work. Using the indicated example, the
the accountcode values become as follows when the peeraccount is set on
SIP/100 before calling SIP/200:
SIP/100 ---> Local;1 ---- Local;2 ---> SIP/200
acct: 100 \/ acct: 200 \/ acct: 100 \/ acct: 200
peer: 200 /\ peer: 100 /\ peer: 200 /\ peer: 100
If a channel already has an accountcode it can only change by the
following explicit user actions:
1) A channel originate method that can specify an accountcode to use.
2) The calling channel propagating its non-empty peeraccount or its
non-empty accountcode if the peeraccount was empty to the outgoing
channel's accountcode before initiating the dial. e.g., Dial and
FollowMe. The exception to this propagation method is Queue. Queue will
only propagate peeraccounts this way only if the outgoing channel does not
have an accountcode.
3) Dialplan using CHANNEL(accountcode).
4) Dialplan using CHANNEL(peeraccount) on the other end of a local
channel pair.
If a channel does not have an accountcode it can get one from the
following places:
1) The channel driver's configuration at channel creation.
2) Explicit user action as already indicated.
3) Entering a basic or stasis-mixing bridge from a peer channel's
peeraccount value.
You can specify the accountcode for an outgoing channel by setting the
CHANNEL(peeraccount) before using the Dial, FollowMe, and Queue
applications. Queue adds the wrinkle that it will not overwrite an
existing accountcode on the outgoing channel with the calling channels
values.
Accountcode and peeraccount values propagate to an outgoing channel before
dialing. Accountcodes also propagate when channels enter or leave a basic
or stasis-mixing bridge. The peeraccount value only makes sense for
mixing bridges with two channels; it is meaningless otherwise.
* Made peeraccount functional by changing accountcode propagation as
described above.
* Fixed CEL extracting the wrong ie value for the peeraccount. This was
done intentionally in Asterisk v1.8 when that version had to punt on
peeraccount.
* Fixed a few places dealing with accountcodes that were reading from
channels without the lock held.
AFS-65 #close
Review: https://reviewboard.asterisk.org/r/3601/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419520 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-24 22:48:38 +00:00
ast_channel_lock_both ( local_chan , chan1 ) ;
ast_channel_req_accountcodes ( local_chan , chan1 , AST_CHANNEL_REQUESTOR_REPLACEMENT ) ;
2013-12-13 20:13:22 +00:00
pbx_builtin_setvar_helper ( local_chan , ATTENDEDTRANSFER , ast_channel_name ( chan1 ) ) ;
accountcode: Slightly change accountcode propagation.
The previous behavior was to simply set the accountcode of an outgoing
channel to the accountcode of the channel initiating the call. It was
done this way a long time ago to allow the accountcode set on the SIP/100
channel to be propagated to a local channel so the dialplan execution on
the Local;2 channel would have the SIP/100 accountcode available.
SIP/100 -> Local;1/Local;2 -> SIP/200
Propagating the SIP/100 accountcode to the local channels is very useful.
Without any dialplan manipulation, all channels in this call would have
the same accountcode.
Using dialplan, you can set a different accountcode on the SIP/200 channel
either by setting the accountcode on the Local;2 channel or by the Dial
application's b(pre-dial), M(macro) or U(gosub) options, or by the
FollowMe application's b(pre-dial) option, or by the Queue application's
macro or gosub options. Before Asterisk v12, the altered accountcode on
SIP/200 will remain until the local channels optimize out and the
accountcode would change to the SIP/100 accountcode.
Asterisk v1.8 attempted to add peeraccount support but ultimately had to
punt on the support. The peeraccount support was rendered useless because
of how the CDR code needed to unconditionally force the caller's
accountcode onto the peer channel's accountcode. The CEL events were thus
intentionally made to always use the channel's accountcode as the
peeraccount value.
With the arrival of Asterisk v12, the situation has improved somewhat so
peeraccount support can be made to work. Using the indicated example, the
the accountcode values become as follows when the peeraccount is set on
SIP/100 before calling SIP/200:
SIP/100 ---> Local;1 ---- Local;2 ---> SIP/200
acct: 100 \/ acct: 200 \/ acct: 100 \/ acct: 200
peer: 200 /\ peer: 100 /\ peer: 200 /\ peer: 100
If a channel already has an accountcode it can only change by the
following explicit user actions:
1) A channel originate method that can specify an accountcode to use.
2) The calling channel propagating its non-empty peeraccount or its
non-empty accountcode if the peeraccount was empty to the outgoing
channel's accountcode before initiating the dial. e.g., Dial and
FollowMe. The exception to this propagation method is Queue. Queue will
only propagate peeraccounts this way only if the outgoing channel does not
have an accountcode.
3) Dialplan using CHANNEL(accountcode).
4) Dialplan using CHANNEL(peeraccount) on the other end of a local
channel pair.
If a channel does not have an accountcode it can get one from the
following places:
1) The channel driver's configuration at channel creation.
2) Explicit user action as already indicated.
3) Entering a basic or stasis-mixing bridge from a peer channel's
peeraccount value.
You can specify the accountcode for an outgoing channel by setting the
CHANNEL(peeraccount) before using the Dial, FollowMe, and Queue
applications. Queue adds the wrinkle that it will not overwrite an
existing accountcode on the outgoing channel with the calling channels
values.
Accountcode and peeraccount values propagate to an outgoing channel before
dialing. Accountcodes also propagate when channels enter or leave a basic
or stasis-mixing bridge. The peeraccount value only makes sense for
mixing bridges with two channels; it is meaningless otherwise.
* Made peeraccount functional by changing accountcode propagation as
described above.
* Fixed CEL extracting the wrong ie value for the peeraccount. This was
done intentionally in Asterisk v1.8 when that version had to punt on
peeraccount.
* Fixed a few places dealing with accountcodes that were reading from
channels without the lock held.
AFS-65 #close
Review: https://reviewboard.asterisk.org/r/3601/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@419520 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-07-24 22:48:38 +00:00
ast_channel_unlock ( local_chan ) ;
ast_channel_unlock ( chan1 ) ;
2013-12-13 20:13:22 +00:00
2013-05-28 14:45:31 +00:00
if ( bridge2 ) {
res = ast_local_setup_bridge ( local_chan , bridge2 , chan2 , NULL ) ;
} else {
2013-06-28 18:42:24 +00:00
app = ast_strdupa ( ast_channel_appl ( chan2 ) ) ;
2013-05-28 14:45:31 +00:00
res = ast_local_setup_masquerade ( local_chan , chan2 ) ;
}
if ( res ) {
ast_hangup ( local_chan ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
2015-07-08 19:56:10 +00:00
/*
* Since bridges need to be unlocked before entering ast_bridge_impart and
* core_local may call into it then the bridges need to be unlocked here .
*/
ast_bridge_unlock ( bridge1 ) ;
if ( bridge2 ) {
ast_bridge_unlock ( bridge2 ) ;
}
2013-05-28 14:45:31 +00:00
if ( ast_call ( local_chan , dest , 0 ) ) {
ast_hangup ( local_chan ) ;
2015-07-08 19:56:10 +00:00
BRIDGE_LOCK_ONE_OR_BOTH ( bridge1 , bridge2 ) ;
2013-05-28 14:45:31 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
}
2014-08-07 15:30:19 +00:00
/* Get a ref for use later since this one is being stolen */
ao2_ref ( local_chan , + 1 ) ;
2013-09-13 22:19:23 +00:00
if ( ast_bridge_impart ( bridge1 , local_chan , chan1 , NULL ,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT ) ) {
2013-05-28 14:45:31 +00:00
ast_hangup ( local_chan ) ;
2014-08-07 15:30:19 +00:00
ao2_cleanup ( local_chan ) ;
2015-07-08 19:56:10 +00:00
BRIDGE_LOCK_ONE_OR_BOTH ( bridge1 , bridge2 ) ;
2013-05-28 14:45:31 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
}
2015-07-08 19:56:10 +00:00
BRIDGE_LOCK_ONE_OR_BOTH ( bridge1 , bridge2 ) ;
2013-05-28 14:45:31 +00:00
2013-06-28 18:42:24 +00:00
if ( bridge2 ) {
2017-06-20 21:05:08 +00:00
void * tech ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
struct ast_channel * locals [ 2 ] ;
2013-06-28 18:42:24 +00:00
2016-03-01 22:18:21 +00:00
/* Have to lock everything just in case a hangup comes in early */
2017-06-20 21:05:08 +00:00
ast_local_lock_all ( local_chan , & tech , & locals [ 0 ] , & locals [ 1 ] ) ;
2016-03-01 22:18:21 +00:00
if ( ! locals [ 0 ] | | ! locals [ 1 ] ) {
ast_log ( LOG_ERROR , " Transfer failed probably due to an early hangup - "
" missing other half of '%s' \n " , ast_channel_name ( local_chan ) ) ;
2017-06-20 21:05:08 +00:00
ast_local_unlock_all ( tech , locals [ 0 ] , locals [ 1 ] ) ;
2016-03-01 22:18:21 +00:00
ao2_cleanup ( local_chan ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
2013-06-28 18:42:24 +00:00
2016-03-01 22:18:21 +00:00
/* Make sure the peer is properly set */
if ( local_chan ! = locals [ 0 ] ) {
SWAP ( locals [ 0 ] , locals [ 1 ] ) ;
}
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
ast_attended_transfer_message_add_link ( transfer_msg , locals ) ;
2017-06-20 21:05:08 +00:00
ast_local_unlock_all ( tech , locals [ 0 ] , locals [ 1 ] ) ;
2013-06-28 18:42:24 +00:00
} else {
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
ast_attended_transfer_message_add_app ( transfer_msg , app , local_chan ) ;
2013-06-28 18:42:24 +00:00
}
2013-05-28 14:45:31 +00:00
2014-08-07 15:30:19 +00:00
ao2_cleanup ( local_chan ) ;
return AST_BRIDGE_TRANSFER_SUCCESS ;
2009-03-05 18:18:27 +00:00
}
2014-05-22 15:52:30 +00:00
static enum ast_transfer_result try_parking ( struct ast_channel * transferer ,
const char * context , const char * exten , transfer_channel_cb new_channel_cb ,
struct transfer_channel_data * user_data_wrapper )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
RAII_VAR ( struct ast_bridge_channel * , transferer_bridge_channel , NULL , ao2_cleanup ) ;
2013-08-01 20:55:17 +00:00
2013-08-17 15:01:54 +00:00
if ( ! ast_parking_provider_registered ( ) ) {
2013-08-01 20:55:17 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
ast_channel_lock ( transferer ) ;
2013-06-06 18:21:18 +00:00
transferer_bridge_channel = ast_channel_get_bridge_channel ( transferer ) ;
2013-05-21 18:00:22 +00:00
ast_channel_unlock ( transferer ) ;
2009-03-05 18:18:27 +00:00
2013-06-06 18:21:18 +00:00
if ( ! transferer_bridge_channel ) {
2013-08-01 20:55:17 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-08-17 15:01:54 +00:00
if ( ast_parking_blind_transfer_park ( transferer_bridge_channel ,
2014-05-22 15:52:30 +00:00
context , exten , new_channel_cb , user_data_wrapper ) ) {
2013-08-01 20:55:17 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-08-01 20:55:17 +00:00
return AST_BRIDGE_TRANSFER_SUCCESS ;
2009-03-05 18:18:27 +00:00
}
2013-12-13 20:13:22 +00:00
void ast_bridge_set_transfer_variables ( struct ast_channel * chan , const char * value , int attended )
{
char * writevar ;
char * erasevar ;
if ( attended ) {
writevar = ATTENDEDTRANSFER ;
erasevar = BLINDTRANSFER ;
} else {
writevar = BLINDTRANSFER ;
erasevar = ATTENDEDTRANSFER ;
}
pbx_builtin_setvar_helper ( chan , writevar , value ) ;
2013-12-16 18:31:12 +00:00
pbx_builtin_setvar_helper ( chan , erasevar , NULL ) ;
2013-12-13 20:13:22 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2013-12-13 20:13:22 +00:00
* \ brief Set the transfer variable as appropriate on channels involved in the transfer
2013-05-21 18:00:22 +00:00
*
2013-12-13 20:13:22 +00:00
* The transferer channel will have its variable set the same as its BRIDGEPEER
2013-05-21 18:00:22 +00:00
* variable . This will account for all channels that it is bridged to . The other channels
2013-12-13 20:13:22 +00:00
* involved in the transfer will have their variable set to the transferer
2013-05-21 18:00:22 +00:00
* channel ' s name .
*
2013-12-13 20:13:22 +00:00
* \ param transferer The channel performing the transfer
2013-05-21 18:00:22 +00:00
* \ param channels The channels belonging to the bridge
2013-12-13 20:13:22 +00:00
* \ param is_attended false set BLINDTRANSFER and unset ATTENDEDTRANSFER
* true set ATTENDEDTRANSFER and unset BLINDTRANSFER
2013-05-21 18:00:22 +00:00
*/
2013-12-13 20:13:22 +00:00
static void set_transfer_variables_all ( struct ast_channel * transferer , struct ao2_container * channels , int is_attended )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
struct ao2_iterator iter ;
struct ast_channel * chan ;
const char * transferer_name ;
const char * transferer_bridgepeer ;
ast_channel_lock ( transferer ) ;
transferer_name = ast_strdupa ( ast_channel_name ( transferer ) ) ;
transferer_bridgepeer = ast_strdupa ( S_OR ( pbx_builtin_getvar_helper ( transferer , " BRIDGEPEER " ) , " " ) ) ;
ast_channel_unlock ( transferer ) ;
for ( iter = ao2_iterator_init ( channels , 0 ) ;
( chan = ao2_iterator_next ( & iter ) ) ;
ao2_cleanup ( chan ) ) {
if ( chan = = transferer ) {
2013-12-13 20:13:22 +00:00
ast_bridge_set_transfer_variables ( chan , transferer_bridgepeer , is_attended ) ;
2013-05-21 18:00:22 +00:00
} else {
2013-12-13 20:13:22 +00:00
ast_bridge_set_transfer_variables ( chan , transferer_name , is_attended ) ;
2013-05-21 18:00:22 +00:00
}
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
ao2_iterator_destroy ( & iter ) ;
}
2009-03-05 18:18:27 +00:00
2013-05-31 15:34:20 +00:00
static struct ast_bridge * acquire_bridge ( struct ast_channel * chan )
{
struct ast_bridge * bridge ;
ast_channel_lock ( chan ) ;
bridge = ast_channel_get_bridge ( chan ) ;
ast_channel_unlock ( chan ) ;
2016-05-09 19:27:53 +00:00
if ( bridge & & ast_test_flag ( & bridge - > feature_flags ,
( AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_INVISIBLE ) ) ) {
2013-05-31 15:34:20 +00:00
ao2_ref ( bridge , - 1 ) ;
bridge = NULL ;
}
return bridge ;
}
2013-06-28 18:42:24 +00:00
enum ast_transfer_result ast_bridge_transfer_blind ( int is_external ,
struct ast_channel * transferer , const char * exten , const char * context ,
2013-05-21 18:00:22 +00:00
transfer_channel_cb new_channel_cb , void * user_data )
{
RAII_VAR ( struct ast_bridge * , bridge , NULL , ao2_cleanup ) ;
2013-05-31 15:34:20 +00:00
RAII_VAR ( struct ast_bridge_channel * , bridge_channel , NULL , ao2_cleanup ) ;
2013-05-21 18:00:22 +00:00
RAII_VAR ( struct ao2_container * , channels , NULL , ao2_cleanup ) ;
2013-07-26 21:34:23 +00:00
RAII_VAR ( struct ast_channel * , transferee , NULL , ast_channel_cleanup ) ;
2014-05-22 15:52:30 +00:00
RAII_VAR ( struct transfer_channel_data * , user_data_wrapper , NULL , ao2_cleanup ) ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
RAII_VAR ( struct ast_blind_transfer_message * , transfer_message , NULL , ao2_cleanup ) ;
2013-05-21 18:00:22 +00:00
int do_bridge_transfer ;
int transfer_prohibited ;
2013-06-28 18:42:24 +00:00
enum ast_transfer_result transfer_result ;
2009-03-05 18:18:27 +00:00
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
transfer_message = ast_blind_transfer_message_create ( is_external , transferer , exten , context ) ;
if ( ! transfer_message ) {
/* Out of memory. Not even possible to publish a Stasis message about the
* failure
*/
ast_log ( LOG_ERROR , " Unable to allocate memory for blind transfer publication from %s \n " ,
ast_channel_name ( transferer ) ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
2013-05-31 15:34:20 +00:00
bridge = acquire_bridge ( transferer ) ;
if ( ! bridge ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_INVALID ;
goto publish ;
2013-05-31 15:34:20 +00:00
}
2014-08-07 15:30:19 +00:00
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
ast_bridge_lock ( bridge ) ;
transfer_message - > bridge = ast_bridge_snapshot_create ( bridge ) ;
ast_bridge_unlock ( bridge ) ;
if ( ! transfer_message - > bridge ) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL ;
goto publish ;
}
2014-08-07 15:30:19 +00:00
transferee = ast_bridge_peer ( bridge , transferer ) ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
if ( transferee ) {
transfer_message - > transferee = ast_channel_snapshot_get_latest ( ast_channel_uniqueid ( transferee ) ) ;
if ( ! transfer_message - > transferee ) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL ;
goto publish ;
}
}
2014-08-07 15:30:19 +00:00
2013-05-21 18:00:22 +00:00
ast_channel_lock ( transferer ) ;
2013-05-31 15:34:20 +00:00
bridge_channel = ast_channel_get_bridge_channel ( transferer ) ;
2013-05-21 18:00:22 +00:00
ast_channel_unlock ( transferer ) ;
2013-05-31 15:34:20 +00:00
if ( ! bridge_channel ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_INVALID ;
goto publish ;
2009-03-05 18:18:27 +00:00
}
2014-05-22 15:52:30 +00:00
user_data_wrapper = ao2_alloc ( sizeof ( * user_data_wrapper ) , NULL ) ;
if ( ! user_data_wrapper ) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL ;
goto publish ;
}
user_data_wrapper - > data = user_data ;
2013-05-31 15:34:20 +00:00
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold ( bridge_channel ) ;
2014-05-22 15:52:30 +00:00
transfer_result = try_parking ( transferer , context , exten , new_channel_cb , user_data_wrapper ) ;
2013-08-01 20:55:17 +00:00
if ( transfer_result = = AST_BRIDGE_TRANSFER_SUCCESS ) {
2013-06-28 18:42:24 +00:00
goto publish ;
2009-03-05 18:18:27 +00:00
}
2014-05-22 15:52:30 +00:00
/* Since parking didn't take control of the user_data_wrapper, we are just going to raise the completed flag now. */
user_data_wrapper - > completed = 1 ;
2013-05-21 18:00:22 +00:00
{
SCOPED_LOCK ( lock , bridge , ast_bridge_lock , ast_bridge_unlock ) ;
2013-06-06 00:16:23 +00:00
2013-05-21 18:00:22 +00:00
channels = ast_bridge_peers_nolock ( bridge ) ;
if ( ! channels ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_FAIL ;
goto publish ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
if ( ao2_container_count ( channels ) < = 1 ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_INVALID ;
goto publish ;
2013-05-21 18:00:22 +00:00
}
transfer_prohibited = ast_test_flag ( & bridge - > feature_flags ,
AST_BRIDGE_FLAG_TRANSFER_PROHIBITED ) ;
do_bridge_transfer = ast_test_flag ( & bridge - > feature_flags ,
AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY ) | |
ao2_container_count ( channels ) > 2 ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
if ( transfer_prohibited ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_NOT_PERMITTED ;
goto publish ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-12-13 20:13:22 +00:00
set_transfer_variables_all ( transferer , channels , 0 ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
if ( do_bridge_transfer ) {
2014-08-20 13:06:33 +00:00
transfer_result = blind_transfer_bridge ( is_external , transferer , bridge ,
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
exten , context , transferee , new_channel_cb , user_data_wrapper , transfer_message ) ;
2013-06-28 18:42:24 +00:00
goto publish ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/* Reaching this portion means that we're dealing with a two-party bridge */
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
if ( ! transferee ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_FAIL ;
goto publish ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-07-25 02:20:23 +00:00
if ( bridge_channel_internal_queue_blind_transfer ( transferee , exten , context ,
2014-05-22 15:52:30 +00:00
new_channel_cb , user_data_wrapper ) ) {
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_FAIL ;
goto publish ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
ast_bridge_remove ( bridge , transferer ) ;
2013-06-28 18:42:24 +00:00
transfer_result = AST_BRIDGE_TRANSFER_SUCCESS ;
publish :
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
transfer_message - > result = transfer_result ;
ast_bridge_publish_blind_transfer ( transfer_message ) ;
2013-06-28 18:42:24 +00:00
return transfer_result ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-28 14:45:31 +00:00
/*!
* \ internal
* \ brief Performs an attended transfer by moving a channel from one bridge to another
*
* The channel that is bridged to the source_channel is moved into the dest_bridge from
* the source_bridge_channel ' s bridge . The swap_channel is swapped out of the dest_bridge and placed in
* the source_bridge_channel ' s bridge .
*
* \ note dest_bridge and source_bridge_channel ' s bridge MUST be locked before calling this function .
*
* \ param dest_bridge The final bridge for the attended transfer
* \ param source_channel Channel who is bridged to the channel that will move
* \ param swap_channel Channel to be swapped out of the dest_bridge
* \ return The success or failure of the swap attempt
*/
static enum ast_transfer_result bridge_swap_attended_transfer ( struct ast_bridge * dest_bridge ,
struct ast_bridge_channel * source_bridge_channel , struct ast_channel * swap_channel )
{
struct ast_bridge_channel * bridged_to_source ;
bridged_to_source = ast_bridge_channel_peer ( source_bridge_channel ) ;
2013-06-06 00:16:23 +00:00
if ( bridged_to_source
2013-07-25 02:20:23 +00:00
& & bridged_to_source - > state = = BRIDGE_CHANNEL_STATE_WAIT
2013-06-06 00:16:23 +00:00
& & ! ast_test_flag ( & bridged_to_source - > features - > feature_flags ,
AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE ) ) {
2013-05-28 14:45:31 +00:00
bridged_to_source - > swap = swap_channel ;
2013-07-24 15:38:18 +00:00
if ( bridge_do_move ( dest_bridge , bridged_to_source , 1 , 0 ) ) {
2013-06-06 00:16:23 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
}
/* Must kick the source channel out of its bridge. */
2013-08-22 21:09:52 +00:00
ast_bridge_channel_leave_bridge ( source_bridge_channel ,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE , AST_CAUSE_NORMAL_CLEARING ) ;
2013-06-06 00:16:23 +00:00
return AST_BRIDGE_TRANSFER_SUCCESS ;
2013-05-28 14:45:31 +00:00
} else {
return AST_BRIDGE_TRANSFER_INVALID ;
}
}
/*!
* \ internal
* \ brief Function that performs an attended transfer when both transferer channels are bridged
*
* The method by which the transfer is performed is dependent on whether the bridges allow for
* optimization to occur between them . If no optimization is permitted , then an unreal channel
* is placed as a link between the two bridges . If optimization is permitted , then that means
* we are free to perform move or merge operations in order to perform the transfer .
*
* \ note to_transferee_bridge and to_target_bridge MUST be locked before calling this function
*
* \ param to_transferee The channel that is bridged to the transferee
* \ param to_transferee_bridge_channel to_transferee ' s bridge_channel
* \ param to_transfer_target The channel that is bridged to the transfer target
* \ param to_target_bridge_channel to_transfer_target ' s bridge_channel
* \ param to_transferee_bridge The bridge between to_transferee and the transferee
* \ param to_target_bridge The bridge between to_transfer_target and the transfer_target
2013-06-28 18:42:24 +00:00
* \ param publication Data to publish for a stasis attended transfer message
2013-05-28 14:45:31 +00:00
* \ return The success or failure of the attended transfer
*/
static enum ast_transfer_result two_bridge_attended_transfer ( struct ast_channel * to_transferee ,
struct ast_bridge_channel * to_transferee_bridge_channel ,
struct ast_channel * to_transfer_target ,
struct ast_bridge_channel * to_target_bridge_channel ,
2013-06-28 18:42:24 +00:00
struct ast_bridge * to_transferee_bridge , struct ast_bridge * to_target_bridge ,
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
struct ast_attended_transfer_message * transfer_msg )
2013-05-28 14:45:31 +00:00
{
struct ast_bridge_channel * kick_me [ ] = {
to_transferee_bridge_channel ,
to_target_bridge_channel ,
} ;
2013-06-28 18:42:24 +00:00
enum ast_transfer_result res ;
struct ast_bridge * final_bridge = NULL ;
2013-12-13 20:13:22 +00:00
RAII_VAR ( struct ao2_container * , channels , NULL , ao2_cleanup ) ;
channels = ast_bridge_peers_nolock ( to_transferee_bridge ) ;
if ( ! channels ) {
res = AST_BRIDGE_TRANSFER_FAIL ;
goto end ;
}
set_transfer_variables_all ( to_transferee , channels , 1 ) ;
2013-05-28 14:45:31 +00:00
switch ( ast_bridges_allow_optimization ( to_transferee_bridge , to_target_bridge ) ) {
case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE :
2013-06-28 18:42:24 +00:00
final_bridge = to_transferee_bridge ;
res = bridge_swap_attended_transfer ( to_transferee_bridge , to_target_bridge_channel , to_transferee ) ;
goto end ;
2013-05-28 14:45:31 +00:00
case AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE :
2013-06-28 18:42:24 +00:00
final_bridge = to_target_bridge ;
res = bridge_swap_attended_transfer ( to_target_bridge , to_transferee_bridge_channel , to_transfer_target ) ;
goto end ;
2013-05-28 14:45:31 +00:00
case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE :
2013-06-28 18:42:24 +00:00
final_bridge = to_transferee_bridge ;
2013-07-24 15:38:18 +00:00
bridge_do_merge ( to_transferee_bridge , to_target_bridge , kick_me , ARRAY_LEN ( kick_me ) , 0 ) ;
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_SUCCESS ;
goto end ;
2013-05-28 14:45:31 +00:00
case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE :
2013-06-28 18:42:24 +00:00
final_bridge = to_target_bridge ;
2013-07-24 15:38:18 +00:00
bridge_do_merge ( to_target_bridge , to_transferee_bridge , kick_me , ARRAY_LEN ( kick_me ) , 0 ) ;
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_SUCCESS ;
goto end ;
2013-05-28 14:45:31 +00:00
case AST_BRIDGE_OPTIMIZE_PROHIBITED :
default :
/* Just because optimization wasn't doable doesn't necessarily mean
* that we can actually perform the transfer . Some reasons for non - optimization
* indicate bridge invalidity , so let ' s check those before proceeding .
*/
if ( to_transferee_bridge - > inhibit_merge | | to_transferee_bridge - > dissolved | |
to_target_bridge - > inhibit_merge | | to_target_bridge - > dissolved ) {
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
return AST_BRIDGE_TRANSFER_INVALID ;
2013-05-28 14:45:31 +00:00
}
2013-06-28 18:42:24 +00:00
2013-05-28 14:45:31 +00:00
return attended_transfer_bridge ( to_transferee , to_transfer_target ,
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
to_transferee_bridge , to_target_bridge , transfer_msg ) ;
2013-05-28 14:45:31 +00:00
}
2013-06-28 18:42:24 +00:00
end :
if ( res = = AST_BRIDGE_TRANSFER_SUCCESS ) {
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
ast_attended_transfer_message_add_merge ( transfer_msg , final_bridge ) ;
2013-06-28 18:42:24 +00:00
}
return res ;
2013-05-28 14:45:31 +00:00
}
2013-05-21 18:00:22 +00:00
enum ast_transfer_result ast_bridge_transfer_attended ( struct ast_channel * to_transferee ,
2013-05-28 14:45:31 +00:00
struct ast_channel * to_transfer_target )
{
RAII_VAR ( struct ast_bridge * , to_transferee_bridge , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_bridge * , to_target_bridge , NULL , ao2_cleanup ) ;
2013-05-31 15:34:20 +00:00
RAII_VAR ( struct ast_bridge_channel * , to_transferee_bridge_channel , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_bridge_channel * , to_target_bridge_channel , NULL , ao2_cleanup ) ;
2013-05-28 14:45:31 +00:00
RAII_VAR ( struct ao2_container * , channels , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_channel * , transferee , NULL , ao2_cleanup ) ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
RAII_VAR ( struct ast_attended_transfer_message * , transfer_msg , NULL , ao2_cleanup ) ;
2014-04-11 12:43:34 +00:00
struct ast_bridge * the_bridge = NULL ;
2013-05-28 14:45:31 +00:00
struct ast_channel * chan_bridged ;
struct ast_channel * chan_unbridged ;
int transfer_prohibited ;
int do_bridge_transfer ;
2013-06-28 18:42:24 +00:00
enum ast_transfer_result res ;
const char * app = NULL ;
2015-06-22 20:11:18 +00:00
int hangup_target = 0 ;
2013-05-28 14:45:31 +00:00
to_transferee_bridge = acquire_bridge ( to_transferee ) ;
to_target_bridge = acquire_bridge ( to_transfer_target ) ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
transfer_msg = ast_attended_transfer_message_create ( 1 , to_transferee , to_transferee_bridge ,
to_transfer_target , to_target_bridge , NULL , NULL ) ;
if ( ! transfer_msg ) {
ast_log ( LOG_ERROR , " Unable to create Stasis publication for attended transfer from %s \n " ,
ast_channel_name ( to_transferee ) ) ;
return AST_BRIDGE_TRANSFER_FAIL ;
}
2013-06-28 18:42:24 +00:00
2013-05-28 14:45:31 +00:00
/* They can't both be unbridged, you silly goose! */
if ( ! to_transferee_bridge & & ! to_target_bridge ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_INVALID ;
goto end ;
2013-05-28 14:45:31 +00:00
}
2013-05-31 15:34:20 +00:00
ast_channel_lock ( to_transferee ) ;
to_transferee_bridge_channel = ast_channel_get_bridge_channel ( to_transferee ) ;
ast_channel_unlock ( to_transferee ) ;
ast_channel_lock ( to_transfer_target ) ;
to_target_bridge_channel = ast_channel_get_bridge_channel ( to_transfer_target ) ;
ast_channel_unlock ( to_transfer_target ) ;
if ( to_transferee_bridge_channel ) {
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold ( to_transferee_bridge_channel ) ;
}
if ( to_target_bridge_channel ) {
2013-06-06 22:46:54 +00:00
const char * target_complete_sound ;
2013-05-31 15:34:20 +00:00
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold ( to_target_bridge_channel ) ;
2013-06-06 22:46:54 +00:00
/* Is there a courtesy sound to play to the target? */
ast_channel_lock ( to_transfer_target ) ;
target_complete_sound = pbx_builtin_getvar_helper ( to_transfer_target ,
" ATTENDED_TRANSFER_COMPLETE_SOUND " ) ;
if ( ! ast_strlen_zero ( target_complete_sound ) ) {
target_complete_sound = ast_strdupa ( target_complete_sound ) ;
} else {
target_complete_sound = NULL ;
}
ast_channel_unlock ( to_transfer_target ) ;
if ( ! target_complete_sound ) {
ast_channel_lock ( to_transferee ) ;
target_complete_sound = pbx_builtin_getvar_helper ( to_transferee ,
" ATTENDED_TRANSFER_COMPLETE_SOUND " ) ;
if ( ! ast_strlen_zero ( target_complete_sound ) ) {
target_complete_sound = ast_strdupa ( target_complete_sound ) ;
} else {
target_complete_sound = NULL ;
}
ast_channel_unlock ( to_transferee ) ;
}
if ( target_complete_sound ) {
ast_bridge_channel_write_playfile ( to_target_bridge_channel , NULL ,
target_complete_sound , NULL ) ;
}
2013-05-31 15:34:20 +00:00
}
2013-05-28 14:45:31 +00:00
/* Let's get the easy one out of the way first */
if ( to_transferee_bridge & & to_target_bridge ) {
2013-05-31 15:34:20 +00:00
if ( ! to_transferee_bridge_channel | | ! to_target_bridge_channel ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_INVALID ;
goto end ;
2013-05-31 15:34:20 +00:00
}
2013-05-28 14:45:31 +00:00
ast_bridge_lock_both ( to_transferee_bridge , to_target_bridge ) ;
res = two_bridge_attended_transfer ( to_transferee , to_transferee_bridge_channel ,
to_transfer_target , to_target_bridge_channel ,
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
to_transferee_bridge , to_target_bridge , transfer_msg ) ;
2013-05-28 14:45:31 +00:00
ast_bridge_unlock ( to_transferee_bridge ) ;
ast_bridge_unlock ( to_target_bridge ) ;
2015-06-22 20:11:18 +00:00
hangup_target = 1 ;
2013-06-28 18:42:24 +00:00
goto end ;
2013-05-28 14:45:31 +00:00
}
the_bridge = to_transferee_bridge ? : to_target_bridge ;
chan_bridged = to_transferee_bridge ? to_transferee : to_transfer_target ;
chan_unbridged = to_transferee_bridge ? to_transfer_target : to_transferee ;
2015-04-16 15:51:50 +00:00
/*
* Race condition makes it possible for app to be NULL , so get the app prior to
* transferring with a fallback of " unknown " .
*/
app = ast_strdupa ( ast_channel_appl ( chan_unbridged ) ? : " unknown " ) ;
2013-05-28 14:45:31 +00:00
{
int chan_count ;
SCOPED_LOCK ( lock , the_bridge , ast_bridge_lock , ast_bridge_unlock ) ;
channels = ast_bridge_peers_nolock ( the_bridge ) ;
if ( ! channels ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_FAIL ;
goto end ;
2013-05-28 14:45:31 +00:00
}
chan_count = ao2_container_count ( channels ) ;
if ( chan_count < = 1 ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_INVALID ;
goto end ;
2013-05-28 14:45:31 +00:00
}
transfer_prohibited = ast_test_flag ( & the_bridge - > feature_flags ,
AST_BRIDGE_FLAG_TRANSFER_PROHIBITED ) ;
do_bridge_transfer = ast_test_flag ( & the_bridge - > feature_flags ,
AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY ) | |
chan_count > 2 ;
}
if ( transfer_prohibited ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_NOT_PERMITTED ;
goto end ;
2013-05-28 14:45:31 +00:00
}
2013-12-13 20:13:22 +00:00
set_transfer_variables_all ( to_transferee , channels , 1 ) ;
2013-05-28 14:45:31 +00:00
if ( do_bridge_transfer ) {
2015-06-22 20:11:18 +00:00
/*
* Hang up the target if it was bridged . Note , if it is not bridged
* it is hung up during the masquerade .
*/
hangup_target = chan_bridged = = to_transfer_target ;
2014-04-11 12:43:34 +00:00
ast_bridge_lock ( the_bridge ) ;
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
res = attended_transfer_bridge ( chan_bridged , chan_unbridged , the_bridge , NULL , transfer_msg ) ;
2014-04-11 12:43:34 +00:00
ast_bridge_unlock ( the_bridge ) ;
goto end ;
2013-05-28 14:45:31 +00:00
}
transferee = get_transferee ( channels , chan_bridged ) ;
if ( ! transferee ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_FAIL ;
goto end ;
2013-05-28 14:45:31 +00:00
}
2013-07-25 02:20:23 +00:00
if ( bridge_channel_internal_queue_attended_transfer ( transferee , chan_unbridged ) ) {
2013-06-28 18:42:24 +00:00
res = AST_BRIDGE_TRANSFER_FAIL ;
goto end ;
2013-05-28 14:45:31 +00:00
}
2009-03-05 18:18:27 +00:00
2013-05-28 14:45:31 +00:00
ast_bridge_remove ( the_bridge , chan_bridged ) ;
2013-06-28 18:42:24 +00:00
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
ast_attended_transfer_message_add_app ( transfer_msg , app , NULL ) ;
2013-08-19 14:54:08 +00:00
res = AST_BRIDGE_TRANSFER_SUCCESS ;
2013-06-28 18:42:24 +00:00
end :
2017-06-20 21:01:48 +00:00
if ( ( res = = AST_BRIDGE_TRANSFER_SUCCESS & & hangup_target ) | | res = = AST_BRIDGE_TRANSFER_FAIL ) {
2015-06-22 20:11:18 +00:00
ast_softhangup ( to_transfer_target , AST_SOFTHANGUP_DEV ) ;
}
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard:
"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?
The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."
The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.
Review: https://reviewboard.asterisk.org/r/4135
........
Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12
........
Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2014-11-14 15:28:42 +00:00
transfer_msg - > result = res ;
ast_bridge_publish_attended_transfer ( transfer_msg ) ;
2013-06-28 18:42:24 +00:00
return res ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Service the bridge manager request .
* \ since 12.0 .0
*
* \ param bridge requesting service .
*
* \ return Nothing
*/
static void bridge_manager_service ( struct ast_bridge * bridge )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
ast_bridge_lock ( bridge ) ;
if ( bridge - > callid ) {
ast_callid_threadassoc_change ( bridge - > callid ) ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/* Do any pending bridge actions. */
bridge_handle_actions ( bridge ) ;
ast_bridge_unlock ( bridge ) ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Bridge manager service thread .
* \ since 12.0 .0
*
* \ return Nothing
*/
static void * bridge_manager_thread ( void * data )
{
struct bridge_manager_controller * manager = data ;
struct bridge_manager_request * request ;
ao2_lock ( manager ) ;
while ( ! manager - > stop ) {
request = AST_LIST_REMOVE_HEAD ( & manager - > service_requests , node ) ;
if ( ! request ) {
ast_cond_wait ( & manager - > cond , ao2_object_get_lockaddr ( manager ) ) ;
continue ;
}
ao2_unlock ( manager ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/* Service the bridge. */
bridge_manager_service ( request - > bridge ) ;
ao2_ref ( request - > bridge , - 1 ) ;
ast_free ( request ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
ao2_lock ( manager ) ;
}
ao2_unlock ( manager ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Destroy the bridge manager controller .
* \ since 12.0 .0
*
* \ param obj Bridge manager to destroy .
*
* \ return Nothing
*/
static void bridge_manager_destroy ( void * obj )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
struct bridge_manager_controller * manager = obj ;
struct bridge_manager_request * request ;
if ( manager - > thread ! = AST_PTHREADT_NULL ) {
/* Stop the manager thread. */
ao2_lock ( manager ) ;
manager - > stop = 1 ;
ast_cond_signal ( & manager - > cond ) ;
ao2_unlock ( manager ) ;
ast_debug ( 1 , " Waiting for bridge manager thread to die. \n " ) ;
pthread_join ( manager - > thread , NULL ) ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/* Destroy the service request queue. */
while ( ( request = AST_LIST_REMOVE_HEAD ( & manager - > service_requests , node ) ) ) {
ao2_ref ( request - > bridge , - 1 ) ;
ast_free ( request ) ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
ast_cond_destroy ( & manager - > cond ) ;
}
/*!
* \ internal
* \ brief Create the bridge manager controller .
* \ since 12.0 .0
*
* \ retval manager on success .
* \ retval NULL on error .
*/
static struct bridge_manager_controller * bridge_manager_create ( void )
{
struct bridge_manager_controller * manager ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
manager = ao2_alloc ( sizeof ( * manager ) , bridge_manager_destroy ) ;
if ( ! manager ) {
/* Well. This isn't good. */
return NULL ;
}
ast_cond_init ( & manager - > cond , NULL ) ;
AST_LIST_HEAD_INIT_NOLOCK ( & manager - > service_requests ) ;
/* Create the bridge manager thread. */
if ( ast_pthread_create ( & manager - > thread , NULL , bridge_manager_thread , manager ) ) {
/* Well. This isn't good either. */
manager - > thread = AST_PTHREADT_NULL ;
ao2_ref ( manager , - 1 ) ;
manager = NULL ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
return manager ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
* \ brief Bridge ao2 container sort function .
* \ since 12.0 .0
*
* \ param obj_left pointer to the ( user - defined part ) of an object .
* \ param obj_right pointer to the ( user - defined part ) of an object .
* \ param flags flags from ao2_callback ( )
* OBJ_POINTER - if set , ' obj_right ' , is an object .
* OBJ_KEY - if set , ' obj_right ' , is a search key item that is not an object .
* OBJ_PARTIAL_KEY - if set , ' obj_right ' , is a partial search key item that is not an object .
*
* \ retval < 0 if obj_left < obj_right
* \ retval = 0 if obj_left = = obj_right
* \ retval > 0 if obj_left > obj_right
*/
static int bridge_sort_cmp ( const void * obj_left , const void * obj_right , int flags )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
const struct ast_bridge * bridge_left = obj_left ;
const struct ast_bridge * bridge_right = obj_right ;
const char * right_key = obj_right ;
int cmp ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
default :
case OBJ_POINTER :
right_key = bridge_right - > uniqueid ;
/* Fall through */
case OBJ_KEY :
cmp = strcmp ( bridge_left - > uniqueid , right_key ) ;
break ;
case OBJ_PARTIAL_KEY :
cmp = strncmp ( bridge_left - > uniqueid , right_key , strlen ( right_key ) ) ;
break ;
}
return cmp ;
2009-03-05 18:18:27 +00:00
}
2013-11-01 21:51:20 +00:00
struct ast_bridge * ast_bridge_find_by_id ( const char * bridge_id )
{
return ao2_find ( bridges , bridge_id , OBJ_SEARCH_KEY ) ;
}
2013-09-12 23:42:23 +00:00
struct bridge_complete {
/*! Nth match to return. */
int state ;
/*! Which match currently on. */
int which ;
} ;
static int complete_bridge_live_search ( void * obj , void * arg , void * data , int flags )
{
struct bridge_complete * search = data ;
if ( + + search - > which > search - > state ) {
return CMP_MATCH ;
}
return 0 ;
}
static char * complete_bridge_live ( const char * word , int state )
{
char * ret ;
struct ast_bridge * bridge ;
struct bridge_complete search = {
. state = state ,
} ;
bridge = ao2_callback_data ( bridges , ast_strlen_zero ( word ) ? 0 : OBJ_PARTIAL_KEY ,
complete_bridge_live_search , ( char * ) word , & search ) ;
if ( ! bridge ) {
return NULL ;
}
ret = ast_strdup ( bridge - > uniqueid ) ;
ao2_ref ( bridge , - 1 ) ;
return ret ;
}
static char * complete_bridge_stasis ( const char * word , int state )
2009-03-05 18:18:27 +00:00
{
2013-07-08 19:19:55 +00:00
char * ret = NULL ;
int wordlen = strlen ( word ) , which = 0 ;
RAII_VAR ( struct ao2_container * , cached_bridges , NULL , ao2_cleanup ) ;
struct ao2_iterator iter ;
struct stasis_message * msg ;
2013-05-21 18:00:22 +00:00
2013-09-12 23:42:23 +00:00
cached_bridges = stasis_cache_dump ( ast_bridge_cache ( ) , ast_bridge_snapshot_type ( ) ) ;
if ( ! cached_bridges ) {
2013-07-08 19:19:55 +00:00
return NULL ;
2013-05-21 18:00:22 +00:00
}
2009-03-05 18:18:27 +00:00
2013-07-08 19:19:55 +00:00
iter = ao2_iterator_init ( cached_bridges , 0 ) ;
for ( ; ( msg = ao2_iterator_next ( & iter ) ) ; ao2_ref ( msg , - 1 ) ) {
struct ast_bridge_snapshot * snapshot = stasis_message_data ( msg ) ;
2013-05-21 18:00:22 +00:00
2013-07-08 19:19:55 +00:00
if ( ! strncasecmp ( word , snapshot - > uniqueid , wordlen ) & & ( + + which > state ) ) {
ret = ast_strdup ( snapshot - > uniqueid ) ;
2013-10-18 18:44:21 +00:00
ao2_ref ( msg , - 1 ) ;
2013-07-08 19:19:55 +00:00
break ;
}
2009-03-05 18:18:27 +00:00
}
2013-07-08 19:19:55 +00:00
ao2_iterator_destroy ( & iter ) ;
2013-05-21 18:00:22 +00:00
return ret ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
static char * handle_bridge_show_all ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
# define FORMAT_HDR "%-36s %5s %-15s %s\n"
# define FORMAT_ROW "%-36s %5u %-15s %s\n"
2013-07-08 19:19:55 +00:00
RAII_VAR ( struct ao2_container * , cached_bridges , NULL , ao2_cleanup ) ;
2013-05-21 18:00:22 +00:00
struct ao2_iterator iter ;
2013-07-08 19:19:55 +00:00
struct stasis_message * msg ;
2013-05-21 18:00:22 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " bridge show all " ;
e - > usage =
" Usage: bridge show all \n "
" List all bridges \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-09-12 23:42:23 +00:00
cached_bridges = stasis_cache_dump ( ast_bridge_cache ( ) , ast_bridge_snapshot_type ( ) ) ;
if ( ! cached_bridges ) {
2013-07-08 19:19:55 +00:00
ast_cli ( a - > fd , " Failed to retrieve cached bridges \n " ) ;
return CLI_SUCCESS ;
}
2013-05-21 18:00:22 +00:00
ast_cli ( a - > fd , FORMAT_HDR , " Bridge-ID " , " Chans " , " Type " , " Technology " ) ;
2013-07-08 19:19:55 +00:00
iter = ao2_iterator_init ( cached_bridges , 0 ) ;
for ( ; ( msg = ao2_iterator_next ( & iter ) ) ; ao2_ref ( msg , - 1 ) ) {
struct ast_bridge_snapshot * snapshot = stasis_message_data ( msg ) ;
2013-05-21 18:00:22 +00:00
ast_cli ( a - > fd , FORMAT_ROW ,
2013-07-08 19:19:55 +00:00
snapshot - > uniqueid ,
snapshot - > num_channels ,
S_OR ( snapshot - > subclass , " <unknown> " ) ,
S_OR ( snapshot - > technology , " <unknown> " ) ) ;
2013-05-21 18:00:22 +00:00
}
ao2_iterator_destroy ( & iter ) ;
return CLI_SUCCESS ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
# undef FORMAT_HDR
# undef FORMAT_ROW
2009-03-05 18:18:27 +00:00
}
2013-07-08 19:19:55 +00:00
/*! \brief Internal callback function for sending channels in a bridge to the CLI */
static int bridge_show_specific_print_channel ( void * obj , void * arg , int flags )
{
const char * uniqueid = obj ;
struct ast_cli_args * a = arg ;
RAII_VAR ( struct stasis_message * , msg , NULL , ao2_cleanup ) ;
struct ast_channel_snapshot * snapshot ;
2013-09-12 23:42:23 +00:00
msg = stasis_cache_get ( ast_channel_cache ( ) , ast_channel_snapshot_type ( ) , uniqueid ) ;
if ( ! msg ) {
2013-07-08 19:19:55 +00:00
return 0 ;
}
snapshot = stasis_message_data ( msg ) ;
ast_cli ( a - > fd , " Channel: %s \n " , snapshot - > name ) ;
return 0 ;
}
2013-05-21 18:00:22 +00:00
static char * handle_bridge_show_specific ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2009-03-05 18:18:27 +00:00
{
2013-07-08 19:19:55 +00:00
RAII_VAR ( struct stasis_message * , msg , NULL , ao2_cleanup ) ;
struct ast_bridge_snapshot * snapshot ;
2013-05-21 18:00:22 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " bridge show " ;
e - > usage =
" Usage: bridge show <bridge-id> \n "
" Show information about the <bridge-id> bridge \n " ;
return NULL ;
case CLI_GENERATE :
if ( a - > pos = = 2 ) {
2013-09-12 23:42:23 +00:00
return complete_bridge_stasis ( a - > word , a - > n ) ;
2013-05-21 18:00:22 +00:00
}
return NULL ;
}
if ( a - > argc ! = 3 ) {
return CLI_SHOWUSAGE ;
2009-03-05 18:18:27 +00:00
}
2013-08-01 13:49:34 +00:00
msg = stasis_cache_get ( ast_bridge_cache ( ) , ast_bridge_snapshot_type ( ) , a - > argv [ 2 ] ) ;
2013-07-08 19:19:55 +00:00
if ( ! msg ) {
2013-05-21 18:00:22 +00:00
ast_cli ( a - > fd , " Bridge '%s' not found \n " , a - > argv [ 2 ] ) ;
return CLI_SUCCESS ;
}
2009-03-05 18:18:27 +00:00
2013-07-08 19:19:55 +00:00
snapshot = stasis_message_data ( msg ) ;
ast_cli ( a - > fd , " Id: %s \n " , snapshot - > uniqueid ) ;
ast_cli ( a - > fd , " Type: %s \n " , S_OR ( snapshot - > subclass , " <unknown> " ) ) ;
ast_cli ( a - > fd , " Technology: %s \n " , S_OR ( snapshot - > technology , " <unknown> " ) ) ;
ast_cli ( a - > fd , " Num-Channels: %u \n " , snapshot - > num_channels ) ;
ao2_callback ( snapshot - > channels , OBJ_NODATA , bridge_show_specific_print_channel , a ) ;
2013-05-21 18:00:22 +00:00
return CLI_SUCCESS ;
2009-03-05 18:18:27 +00:00
}
2014-07-25 10:49:52 +00:00
# ifdef AST_DEVMODE
2013-05-21 18:00:22 +00:00
static char * handle_bridge_destroy_specific ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
struct ast_bridge * bridge ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " bridge destroy " ;
e - > usage =
" Usage: bridge destroy <bridge-id> \n "
" Destroy the <bridge-id> bridge \n " ;
return NULL ;
case CLI_GENERATE :
if ( a - > pos = = 2 ) {
2013-09-12 23:42:23 +00:00
return complete_bridge_live ( a - > word , a - > n ) ;
2013-05-21 18:00:22 +00:00
}
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
if ( a - > argc ! = 3 ) {
return CLI_SHOWUSAGE ;
}
2009-03-05 18:18:27 +00:00
2013-11-01 21:51:20 +00:00
bridge = ast_bridge_find_by_id ( a - > argv [ 2 ] ) ;
2013-05-21 18:00:22 +00:00
if ( ! bridge ) {
ast_cli ( a - > fd , " Bridge '%s' not found \n " , a - > argv [ 2 ] ) ;
return CLI_SUCCESS ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
ast_cli ( a - > fd , " Destroying bridge '%s' \n " , a - > argv [ 2 ] ) ;
2013-08-22 21:09:52 +00:00
ast_bridge_destroy ( bridge , 0 ) ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
return CLI_SUCCESS ;
2011-04-21 18:11:40 +00:00
}
2014-07-25 10:49:52 +00:00
# endif
2011-04-21 18:11:40 +00:00
2013-05-21 18:00:22 +00:00
static char * complete_bridge_participant ( const char * bridge_name , const char * line , const char * word , int pos , int state )
2009-03-05 18:18:27 +00:00
{
2014-07-25 10:49:52 +00:00
struct ast_bridge * bridge ;
2013-05-21 18:00:22 +00:00
struct ast_bridge_channel * bridge_channel ;
int which ;
int wordlen ;
2013-11-01 21:51:20 +00:00
bridge = ast_bridge_find_by_id ( bridge_name ) ;
2013-05-21 18:00:22 +00:00
if ( ! bridge ) {
return NULL ;
2013-01-09 22:56:08 +00:00
}
2013-05-21 18:00:22 +00:00
{
SCOPED_LOCK ( bridge_lock , bridge , ast_bridge_lock , ast_bridge_unlock ) ;
which = 0 ;
wordlen = strlen ( word ) ;
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
if ( ! strncasecmp ( ast_channel_name ( bridge_channel - > chan ) , word , wordlen )
& & + + which > state ) {
2014-07-25 10:49:52 +00:00
ao2_ref ( bridge , - 1 ) ;
2013-05-21 18:00:22 +00:00
return ast_strdup ( ast_channel_name ( bridge_channel - > chan ) ) ;
}
2009-03-05 18:18:27 +00:00
}
}
2014-07-25 10:49:52 +00:00
ao2_ref ( bridge , - 1 ) ;
2013-05-21 18:00:22 +00:00
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
static char * handle_bridge_kick_channel ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2009-03-05 18:18:27 +00:00
{
2017-02-11 15:57:03 +00:00
static const char * const completions [ ] = { " all " , NULL } ;
char * complete ;
2014-07-25 10:49:52 +00:00
struct ast_bridge * bridge ;
2013-05-21 18:00:22 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " bridge kick " ;
e - > usage =
2014-07-25 10:49:52 +00:00
" Usage: bridge kick <bridge-id> <channel-name | all> \n "
" Kick the <channel-name> channel out of the <bridge-id> bridge \n "
" If all is specified as the channel name then all channels will be \n "
" kicked out of the bridge. \n " ;
2013-05-21 18:00:22 +00:00
return NULL ;
case CLI_GENERATE :
if ( a - > pos = = 2 ) {
2013-09-12 23:42:23 +00:00
return complete_bridge_live ( a - > word , a - > n ) ;
2013-05-21 18:00:22 +00:00
}
if ( a - > pos = = 3 ) {
2017-02-11 15:57:03 +00:00
complete = ast_cli_complete ( a - > word , completions , a - > n ) ;
if ( ! complete ) {
complete = complete_bridge_participant ( a - > argv [ 2 ] , a - > line , a - > word , a - > pos , a - > n - 1 ) ;
}
return complete ;
2013-05-21 18:00:22 +00:00
}
return NULL ;
}
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
if ( a - > argc ! = 4 ) {
return CLI_SHOWUSAGE ;
}
2013-11-01 21:51:20 +00:00
bridge = ast_bridge_find_by_id ( a - > argv [ 2 ] ) ;
2013-05-21 18:00:22 +00:00
if ( ! bridge ) {
ast_cli ( a - > fd , " Bridge '%s' not found \n " , a - > argv [ 2 ] ) ;
return CLI_SUCCESS ;
}
2009-03-05 18:18:27 +00:00
2014-07-25 10:49:52 +00:00
if ( ! strcasecmp ( a - > argv [ 3 ] , " all " ) ) {
struct ast_bridge_channel * bridge_channel ;
2009-03-05 18:18:27 +00:00
2014-07-25 10:49:52 +00:00
ast_cli ( a - > fd , " Kicking all channels from bridge '%s' \n " , a - > argv [ 2 ] ) ;
2013-05-21 18:00:22 +00:00
2014-07-25 10:49:52 +00:00
ast_bridge_lock ( bridge ) ;
AST_LIST_TRAVERSE ( & bridge - > channels , bridge_channel , entry ) {
ast_bridge_channel_queue_callback ( bridge_channel , 0 , kick_it , NULL , 0 ) ;
}
ast_bridge_unlock ( bridge ) ;
} else {
struct ast_channel * chan ;
chan = ast_channel_get_by_name_prefix ( a - > argv [ 3 ] , strlen ( a - > argv [ 3 ] ) ) ;
if ( ! chan ) {
ast_cli ( a - > fd , " Channel '%s' not found \n " , a - > argv [ 3 ] ) ;
ao2_ref ( bridge , - 1 ) ;
return CLI_SUCCESS ;
}
ast_cli ( a - > fd , " Kicking channel '%s' from bridge '%s' \n " ,
ast_channel_name ( chan ) , a - > argv [ 2 ] ) ;
ast_bridge_kick ( bridge , chan ) ;
ast_channel_unref ( chan ) ;
}
ao2_ref ( bridge , - 1 ) ;
2013-05-21 18:00:22 +00:00
return CLI_SUCCESS ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
/*! Bridge technology capabilities to string. */
static const char * tech_capability2str ( uint32_t capabilities )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
const char * type ;
if ( capabilities & AST_BRIDGE_CAPABILITY_HOLDING ) {
type = " Holding " ;
} else if ( capabilities & AST_BRIDGE_CAPABILITY_EARLY ) {
type = " Early " ;
} else if ( capabilities & AST_BRIDGE_CAPABILITY_NATIVE ) {
type = " Native " ;
} else if ( capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX ) {
type = " 1to1Mix " ;
} else if ( capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX ) {
type = " MultiMix " ;
} else {
type = " <Unknown> " ;
2011-04-21 18:11:40 +00:00
}
2013-05-21 18:00:22 +00:00
return type ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
static char * handle_bridge_technology_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2009-03-05 18:18:27 +00:00
{
2013-05-21 18:00:22 +00:00
# define FORMAT_HDR "%-20s %-20s %8s %s\n"
2014-05-09 22:49:26 +00:00
# define FORMAT_ROW "%-20s %-20s %8u %s\n"
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
struct ast_bridge_technology * cur ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
switch ( cmd ) {
case CLI_INIT :
e - > command = " bridge technology show " ;
e - > usage =
" Usage: bridge technology show \n "
" List registered bridge technologies \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
2009-03-05 18:18:27 +00:00
}
2013-05-21 18:00:22 +00:00
ast_cli ( a - > fd , FORMAT_HDR , " Name " , " Type " , " Priority " , " Suspended " ) ;
AST_RWLIST_RDLOCK ( & bridge_technologies ) ;
AST_RWLIST_TRAVERSE ( & bridge_technologies , cur , entry ) {
const char * type ;
2009-03-05 18:18:27 +00:00
2013-05-21 18:00:22 +00:00
/* Decode type for display */
type = tech_capability2str ( cur - > capabilities ) ;
2011-04-21 18:11:40 +00:00
2013-05-21 18:00:22 +00:00
ast_cli ( a - > fd , FORMAT_ROW , cur - > name , type , cur - > preference ,
AST_CLI_YESNO ( cur - > suspended ) ) ;
}
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
return CLI_SUCCESS ;
# undef FORMAT
2011-04-21 18:11:40 +00:00
}
2013-05-21 18:00:22 +00:00
static char * complete_bridge_technology ( const char * word , int state )
2011-04-21 18:11:40 +00:00
{
2013-05-21 18:00:22 +00:00
struct ast_bridge_technology * cur ;
char * res ;
int which ;
int wordlen ;
2011-04-21 18:11:40 +00:00
2013-05-21 18:00:22 +00:00
which = 0 ;
wordlen = strlen ( word ) ;
AST_RWLIST_RDLOCK ( & bridge_technologies ) ;
AST_RWLIST_TRAVERSE ( & bridge_technologies , cur , entry ) {
if ( ! strncasecmp ( cur - > name , word , wordlen ) & & + + which > state ) {
res = ast_strdup ( cur - > name ) ;
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
return res ;
}
}
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
return NULL ;
2011-04-21 18:11:40 +00:00
}
2011-06-30 20:33:15 +00:00
2013-05-21 18:00:22 +00:00
static char * handle_bridge_technology_suspend ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
2011-06-30 20:33:15 +00:00
{
2013-05-21 18:00:22 +00:00
struct ast_bridge_technology * cur ;
int suspend ;
int successful ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " bridge technology {suspend|unsuspend} " ;
e - > usage =
" Usage: bridge technology {suspend|unsuspend} <technology-name> \n "
" Suspend or unsuspend a bridge technology. \n " ;
return NULL ;
case CLI_GENERATE :
if ( a - > pos = = 3 ) {
return complete_bridge_technology ( a - > word , a - > n ) ;
2011-06-30 20:33:15 +00:00
}
2013-05-21 18:00:22 +00:00
return NULL ;
}
if ( a - > argc ! = 4 ) {
return CLI_SHOWUSAGE ;
}
suspend = ! strcasecmp ( a - > argv [ 2 ] , " suspend " ) ;
successful = 0 ;
AST_RWLIST_WRLOCK ( & bridge_technologies ) ;
AST_RWLIST_TRAVERSE ( & bridge_technologies , cur , entry ) {
if ( ! strcasecmp ( cur - > name , a - > argv [ 3 ] ) ) {
successful = 1 ;
if ( suspend ) {
ast_bridge_technology_suspend ( cur ) ;
} else {
ast_bridge_technology_unsuspend ( cur ) ;
}
break ;
2011-06-30 20:33:15 +00:00
}
2013-05-21 18:00:22 +00:00
}
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
if ( successful ) {
if ( suspend ) {
ast_cli ( a - > fd , " Suspended bridge technology '%s' \n " , a - > argv [ 3 ] ) ;
} else {
ast_cli ( a - > fd , " Unsuspended bridge technology '%s' \n " , a - > argv [ 3 ] ) ;
2011-07-11 18:44:06 +00:00
}
2013-05-21 18:00:22 +00:00
} else {
ast_cli ( a - > fd , " Bridge technology '%s' not found \n " , a - > argv [ 3 ] ) ;
2011-06-30 20:33:15 +00:00
}
2013-05-21 18:00:22 +00:00
return CLI_SUCCESS ;
2011-06-30 20:33:15 +00:00
}
2013-05-21 18:00:22 +00:00
static struct ast_cli_entry bridge_cli [ ] = {
AST_CLI_DEFINE ( handle_bridge_show_all , " List all bridges " ) ,
AST_CLI_DEFINE ( handle_bridge_show_specific , " Show information about a bridge " ) ,
2014-07-25 10:49:52 +00:00
# ifdef AST_DEVMODE
2013-05-21 18:00:22 +00:00
AST_CLI_DEFINE ( handle_bridge_destroy_specific , " Destroy a bridge " ) ,
2014-07-25 10:49:52 +00:00
# endif
2013-05-21 18:00:22 +00:00
AST_CLI_DEFINE ( handle_bridge_kick_channel , " Kick a channel from a bridge " ) ,
AST_CLI_DEFINE ( handle_bridge_technology_show , " List registered bridge technologies " ) ,
AST_CLI_DEFINE ( handle_bridge_technology_suspend , " Suspend/unsuspend a bridge technology " ) ,
} ;
2013-11-01 21:51:20 +00:00
static int handle_manager_bridge_tech_suspend ( struct mansession * s , const struct message * m , int suspend )
{
const char * name = astman_get_header ( m , " BridgeTechnology " ) ;
struct ast_bridge_technology * cur ;
int successful = 0 ;
if ( ast_strlen_zero ( name ) ) {
astman_send_error ( s , m , " BridgeTechnology must be provided " ) ;
return 0 ;
}
AST_RWLIST_RDLOCK ( & bridge_technologies ) ;
AST_RWLIST_TRAVERSE ( & bridge_technologies , cur , entry ) {
if ( ! strcasecmp ( cur - > name , name ) ) {
successful = 1 ;
if ( suspend ) {
ast_bridge_technology_suspend ( cur ) ;
} else {
ast_bridge_technology_unsuspend ( cur ) ;
}
break ;
}
}
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
if ( ! successful ) {
astman_send_error ( s , m , " BridgeTechnology not found " ) ;
return 0 ;
}
astman_send_ack ( s , m , ( suspend ? " Suspended bridge technology " : " Unsuspended bridge technology " ) ) ;
return 0 ;
}
static int manager_bridge_tech_suspend ( struct mansession * s , const struct message * m )
{
return handle_manager_bridge_tech_suspend ( s , m , 1 ) ;
}
static int manager_bridge_tech_unsuspend ( struct mansession * s , const struct message * m )
{
return handle_manager_bridge_tech_suspend ( s , m , 0 ) ;
}
static int manager_bridge_tech_list ( struct mansession * s , const struct message * m )
{
const char * id = astman_get_header ( m , " ActionID " ) ;
RAII_VAR ( struct ast_str * , id_text , ast_str_create ( 128 ) , ast_free ) ;
struct ast_bridge_technology * cur ;
2015-01-09 18:16:54 +00:00
int num_items = 0 ;
2013-11-01 21:51:20 +00:00
if ( ! id_text ) {
astman_send_error ( s , m , " Internal error " ) ;
return - 1 ;
}
if ( ! ast_strlen_zero ( id ) ) {
ast_str_set ( & id_text , 0 , " ActionID: %s \r \n " , id ) ;
}
2015-01-12 18:09:27 +00:00
astman_send_listack ( s , m , " Bridge technology listing will follow " , " start " ) ;
2013-11-01 21:51:20 +00:00
AST_RWLIST_RDLOCK ( & bridge_technologies ) ;
AST_RWLIST_TRAVERSE ( & bridge_technologies , cur , entry ) {
const char * type ;
type = tech_capability2str ( cur - > capabilities ) ;
astman_append ( s ,
" Event: BridgeTechnologyListItem \r \n "
" BridgeTechnology: %s \r \n "
" BridgeType: %s \r \n "
2014-05-09 22:49:26 +00:00
" BridgePriority: %u \r \n "
2013-11-01 21:51:20 +00:00
" BridgeSuspended: %s \r \n "
" %s "
" \r \n " ,
cur - > name , type , cur - > preference , AST_YESNO ( cur - > suspended ) ,
ast_str_buffer ( id_text ) ) ;
2015-01-09 18:16:54 +00:00
+ + num_items ;
2013-11-01 21:51:20 +00:00
}
AST_RWLIST_UNLOCK ( & bridge_technologies ) ;
2015-01-09 18:16:54 +00:00
astman_send_list_complete_start ( s , m , " BridgeTechnologyListComplete " , num_items ) ;
astman_send_list_complete_end ( s ) ;
2013-11-01 21:51:20 +00:00
return 0 ;
}
2013-09-12 23:36:33 +00:00
/*!
* \ internal
* \ brief Print bridge object key ( name ) .
* \ since 12.0 .0
*
* \ param v_obj A pointer to the object we want the key printed .
* \ param where User data needed by prnt to determine where to put output .
* \ param prnt Print output callback function to use .
*
* \ return Nothing
*/
static void bridge_prnt_obj ( void * v_obj , void * where , ao2_prnt_fn * prnt )
{
struct ast_bridge * bridge = v_obj ;
if ( ! bridge ) {
return ;
}
2014-05-09 22:49:26 +00:00
prnt ( where , " %s %s chans:%u " ,
2013-09-12 23:36:33 +00:00
bridge - > uniqueid , bridge - > v_table - > name , bridge - > num_channels ) ;
}
2013-05-21 18:00:22 +00:00
/*!
* \ internal
2015-03-26 22:24:26 +00:00
* \ brief Shutdown the bridging system . Stuff to do on graceful shutdown .
* \ since 13.3 .0
2013-05-21 18:00:22 +00:00
*
* \ return Nothing
*/
2015-03-26 22:24:26 +00:00
static void bridge_cleanup ( void )
2011-06-30 20:33:15 +00:00
{
2013-11-01 21:51:20 +00:00
ast_manager_unregister ( " BridgeTechnologyList " ) ;
ast_manager_unregister ( " BridgeTechnologySuspend " ) ;
ast_manager_unregister ( " BridgeTechnologyUnsuspend " ) ;
2013-05-21 18:00:22 +00:00
ast_cli_unregister_multiple ( bridge_cli , ARRAY_LEN ( bridge_cli ) ) ;
2013-09-12 23:36:33 +00:00
ao2_container_unregister ( " bridges " ) ;
2015-02-11 17:39:13 +00:00
2013-05-21 18:00:22 +00:00
ao2_cleanup ( bridges ) ;
bridges = NULL ;
ao2_cleanup ( bridge_manager ) ;
bridge_manager = NULL ;
2011-06-30 20:33:15 +00:00
}
2013-05-21 18:00:22 +00:00
int ast_bridging_init ( void )
2011-06-30 20:33:15 +00:00
{
2015-02-11 17:39:13 +00:00
ast_register_cleanup ( bridge_cleanup ) ;
Avoid unnecessary cleanups during immediate shutdown
This patch addresses issues during immediate shutdowns, where modules
are not unloaded, but Asterisk atexit handlers are run.
In the typical case, this usually isn't a big deal. But the
introduction of the Stasis message bus makes it much more likely for
asynchronous activity to be happening off in some thread during
shutdown.
During an immediate shutdown, Asterisk skips unloading modules. But
while it is processing the atexit handlers, there is a window of time
where some of the core message types have been cleaned up, but the
message bus is still running. Specifically, it's still running
module subscriptions that might be using the core message types. If a
message is received by that subscription in that window, it will
attempt to use a message type that has been cleaned up.
To solve this problem, this patch introduces ast_register_cleanup().
This function operates identically to ast_register_atexit(), except
that cleanup calls are not invoked on an immediate shutdown. All of
the core message type and topic cleanup was moved from atexit handlers
to cleanup handlers.
This ensures that core type and topic cleanup only happens if the
modules that used them are first unloaded.
This patch also changes the ast_assert() when accessing a cleaned up
or uninitialized message type to an error log message. Message type
functions are actually NULL safe across the board, so the assert was a
bit heavy handed. Especially for anyone with DO_CRASH enabled.
Review: https://reviewboard.asterisk.org/r/2562/
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390122 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2013-05-30 17:05:53 +00:00
2013-05-21 18:00:22 +00:00
if ( ast_stasis_bridging_init ( ) ) {
return - 1 ;
2011-06-30 20:33:15 +00:00
}
2013-05-21 18:00:22 +00:00
bridge_manager = bridge_manager_create ( ) ;
if ( ! bridge_manager ) {
return - 1 ;
2011-07-11 18:44:06 +00:00
}
2013-05-21 18:00:22 +00:00
bridges = ao2_container_alloc_rbtree ( AO2_ALLOC_OPT_LOCK_MUTEX ,
AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE , bridge_sort_cmp , NULL ) ;
if ( ! bridges ) {
return - 1 ;
2011-06-30 20:33:15 +00:00
}
2013-09-12 23:36:33 +00:00
ao2_container_register ( " bridges " , bridges , bridge_prnt_obj ) ;
2011-06-30 20:33:15 +00:00
2013-05-21 18:00:22 +00:00
ast_bridging_init_basic ( ) ;
2011-07-11 18:44:06 +00:00
2013-05-21 18:00:22 +00:00
ast_cli_register_multiple ( bridge_cli , ARRAY_LEN ( bridge_cli ) ) ;
2011-06-30 20:33:15 +00:00
2013-11-01 21:51:20 +00:00
ast_manager_register_xml_core ( " BridgeTechnologyList " , 0 , manager_bridge_tech_list ) ;
ast_manager_register_xml_core ( " BridgeTechnologySuspend " , 0 , manager_bridge_tech_suspend ) ;
ast_manager_register_xml_core ( " BridgeTechnologyUnsuspend " , 0 , manager_bridge_tech_unsuspend ) ;
2013-05-21 18:00:22 +00:00
return 0 ;
2011-06-30 20:33:15 +00:00
}