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-13 20:15:58 -05: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-13 20:15:58 -05: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-13 20:15:58 -05: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"
ASTERISK_FILE_VERSION ( __FILE__ , "$Revision$" )
#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 13:20:23 -05: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 13:20:23 -05: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 ;
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
}
2013-07-24 15:38:18 +00:00
if ( bridge -> callid ) {
bridge -> callid = ast_callid_unref ( bridge -> callid );
}
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 ;
2013-07-24 15:38:18 +00:00
return bridge ;
2009-03-05 18:18:27 +00:00
}
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
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 ;
2013-08-01 13:49:34 +00:00
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-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
2013-07-24 15:38:18 +00:00
if ( ! ast_bridge_topic ( self )) {
ao2_ref ( self , - 1 );
return NULL ;
}
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:02:41 +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:02:41 +00:00
. push_peek = bridge_base_push_peek ,
2013-05-21 18:00:22 +00:00
};
2009-03-05 18:18:27 +00:00
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 );
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:03 +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:03 +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 16:11:07 -06: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 16:11:07 -06: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 16:11:07 -06: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 16:11:07 -06: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 16:11:07 -06: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 ) {
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 16:58:32 -05: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 ;
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:24:28 +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:24:28 +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 );
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:02:41 +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 );
}
2013-08-12 15:59:19 +00:00
if ( ! res ) {
2016-04-19 16:58:32 -05:00
res = bridge_channel_internal_join ( bridge_channel );
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:24:28 +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 );
join_exit :;
2013-07-25 02:20:23 +00:00
ast_bridge_run_after_callback ( chan );
2016-04-19 16:58:32 -05: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 16:58:32 -05:00
struct ast_bridge_channel * bridge_channel = data ;
2017-09-01 04:17:02 -06: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 04:17:02 -06:00
res = bridge_channel_internal_join ( bridge_channel );
2013-05-21 18:00:22 +00:00
2015-01-22 19:24:28 +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 04:17:02 -06: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 16:58:32 -05: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 16:58:32 -05: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 16:58:32 -05: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:24:28 +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 16:58:32 -05: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 16:58:32 -05: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 16:58:32 -05:00
enum ast_bridge_impart_flags flags ,
struct bridge_channel_impart_cond * cond )
2013-05-21 18:00:22 +00:00
{
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 ;
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:40:59 +00:00
ast_bridge_features_destroy ( features );
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 );
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:24:28 +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:02:41 +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 */
2013-08-12 15:59:19 +00:00
if ( ! res ) {
2016-04-19 16:58:32 -05: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 ) {
2013-08-12 15:59:19 +00:00
res = ast_pthread_create_detached ( & bridge_channel -> thread , NULL ,
2016-04-19 16:58:32 -05:00
bridge_channel_ind_thread , bridge_channel );
2013-08-12 15:59:19 +00:00
} else {
res = ast_pthread_create ( & bridge_channel -> thread , NULL ,
2016-04-19 16:58:32 -05:00
bridge_channel_depart_thread , bridge_channel );
2013-08-12 15:59:19 +00:00
}
2015-07-08 14:56:10 -05:00
if ( ! res ) {
2016-04-19 16:58:32 -05:00
bridge_channel_impart_wait ( cond );
2015-07-08 14:56:10 -05: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:24:28 +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 16:58:32 -05: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:31:08 +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:24:28 +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:24:28 +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-19 19:06:14 -06: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-19 19:06:14 -06: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 ) {
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 13:50:04 -05: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 );
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 13:50:04 -05: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 )) {
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 );
}
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
}
}
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 15:36:42 -05: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 10:11:41 -06: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 );
}
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 */
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 15:36:42 -05: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 10:11:41 -06: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 15:36:42 -05: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 10:11:41 -06: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 ++ ;
}
}
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 ;
}
}
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
}
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 10:11:41 -06: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" ;
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:04:30 +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
2014-11-14 15:24:48 +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:04:30 +00:00
*
2013-05-21 18:00:22 +00:00
* \return The success or failure of the operation
*/
2014-08-20 13:04:30 +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 ,
2014-11-14 15:24:48 +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 );
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
2014-07-24 22:48:38 +00:00
ast_channel_lock_both ( local , transferer );
ast_channel_req_accountcodes ( local , transferer , AST_CHANNEL_REQUESTOR_REPLACEMENT );
2014-11-14 15:24:48 +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 ));
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 ;
}
2014-11-14 15:24:48 +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 ;
}
2014-11-14 15:24:48 +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 ,
2014-11-14 15:24:48 +00:00
struct ast_attended_transfer_message * transfer_msg )
2013-05-28 14:45:31 +00:00
{
2015-07-08 14:56:10 -05: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
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 ;
}
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 ));
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 14:56:10 -05: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 14:56:10 -05: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 14:56:10 -05:00
BRIDGE_LOCK_ONE_OR_BOTH ( bridge1 , bridge2 );
2013-05-28 14:45:31 +00:00
return AST_BRIDGE_TRANSFER_FAIL ;
}
2015-07-08 14:56:10 -05: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 15:41:14 -05:00
void * tech ;
2014-11-14 15:24:48 +00:00
struct ast_channel * locals [ 2 ];
2013-06-28 18:42:24 +00:00
2016-03-01 16:18:21 -06:00
/* Have to lock everything just in case a hangup comes in early */
2017-06-20 15:41:14 -05:00
ast_local_lock_all2 ( local_chan , & tech , & locals [ 0 ], & locals [ 1 ]);
2016-03-01 16:18:21 -06: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 15:41:14 -05:00
ast_local_unlock_all2 ( tech , locals [ 0 ], locals [ 1 ]);
2016-03-01 16:18:21 -06:00
ao2_cleanup ( local_chan );
return AST_BRIDGE_TRANSFER_FAIL ;
}
2013-06-28 18:42:24 +00:00
2016-03-01 16:18:21 -06:00
/* Make sure the peer is properly set */
if ( local_chan != locals [ 0 ]) {
SWAP ( locals [ 0 ], locals [ 1 ]);
}
2014-11-14 15:24:48 +00:00
ast_attended_transfer_message_add_link ( transfer_msg , locals );
2017-06-20 15:41:14 -05:00
ast_local_unlock_all2 ( tech , locals [ 0 ], locals [ 1 ]);
2013-06-28 18:42:24 +00:00
} else {
2014-11-14 15:24:48 +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 );
if ( bridge
&& ast_test_flag ( & bridge -> feature_flags , AST_BRIDGE_FLAG_MASQUERADE_ONLY )) {
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 );
2014-11-14 15:24:48 +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
2014-11-14 15:24:48 +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
2014-11-14 15:24:48 +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 );
2014-11-14 15:24:48 +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:04:30 +00:00
transfer_result = blind_transfer_bridge ( is_external , transferer , bridge ,
2014-11-14 15:24:48 +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 :
2014-11-14 15:24:48 +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 ,
2014-11-14 15:24:48 +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 ) {
2014-11-14 15:24:48 +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 ,
2014-11-14 15:24:48 +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 ) {
2014-11-14 15:24:48 +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 );
2014-11-14 15:24:48 +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 15:11:18 -05: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 );
2014-11-14 15:24:48 +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 ,
2014-11-14 15:24:48 +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 15:11:18 -05: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 10:51:50 -05: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 15:11:18 -05: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 );
2014-11-14 15:24:48 +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
2014-11-14 15:24:48 +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 16:01:48 -05:00
if (( res == AST_BRIDGE_TRANSFER_SUCCESS && hangup_target ) || res == AST_BRIDGE_TRANSFER_FAIL ) {
2015-06-22 15:11:18 -05:00
ast_softhangup ( to_transfer_target , AST_SOFTHANGUP_DEV );
}
2014-11-14 15:24:48 +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 10:57:03 -05: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 10:57:03 -05: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 17:54:49 +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-09 17:54:49 +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 17:54:49 +00:00
++ num_items ;
2013-11-01 21:51:20 +00:00
}
AST_RWLIST_UNLOCK ( & bridge_technologies );
2015-01-09 17:54:49 +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:19:21 +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:19:21 +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:28: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:28:13 +00:00
ast_register_cleanup ( bridge_cleanup );
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
}