@ -47,6 +47,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include "asterisk/app.h"
# include "asterisk/app.h"
# include "asterisk/bridge.h"
# include "asterisk/bridge.h"
# include "asterisk/musiconhold.h"
# include "asterisk/musiconhold.h"
# include "asterisk/astobj2.h"
/*** DOCUMENTATION
/*** DOCUMENTATION
< application name = " BridgeWait " language = " en_US " >
< application name = " BridgeWait " language = " en_US " >
@ -54,6 +55,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Put a call into the holding bridge .
Put a call into the holding bridge .
< / synopsis >
< / synopsis >
< syntax >
< syntax >
< parameter name = " name " >
< para > Name of the holding bridge to join . This is a handle for < literal > BridgeWait < / literal >
only and does not affect the actual bridges that are created . If not provided ,
the reserved name < literal > default < / literal > will be used .
< / para >
< / parameter >
< parameter name = " role " required = " false " >
< parameter name = " role " required = " false " >
< para > Defines the channel ' s purpose for entering the holding bridge . Values are case sensitive .
< para > Defines the channel ' s purpose for entering the holding bridge . Values are case sensitive .
< / para >
< / para >
@ -99,19 +106,81 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
< / syntax >
< / syntax >
< description >
< description >
< para > This application places the incoming channel into a holding bridge .
< para > This application places the incoming channel into a holding bridge .
The channel will then wait in the holding bridge until some
The channel will then wait in the holding bridge until some event occurs
event occurs which removes it from the holding bridge . < / para >
which removes it from the holding bridge . < / para >
< note > < para > This application will answer calls which haven ' t already
been answered . < / para > < / note >
< / description >
< / description >
< / application >
< / application >
* * */
* * */
/* BUGBUG Add bridge name/id parameter to specify which holding bridge to join (required) */
/* BUGBUG The channel may or may not be answered with the r option. */
/* BUGBUG You should not place an announcer into a holding bridge with unanswered channels. */
static char * app = " BridgeWait " ;
# define APP_NAME "BridgeWait"
static struct ast_bridge * holding_bridge ;
# define DEFAULT_BRIDGE_NAME "default"
static struct ao2_container * wait_bridge_wrappers ;
struct wait_bridge_wrapper {
struct ast_bridge * bridge ; /*!< Bridge being wrapped by this wrapper */
char name [ 0 ] ; /*!< Name of the holding bridge wrapper */
} ;
static void wait_bridge_wrapper_destructor ( void * obj )
{
struct wait_bridge_wrapper * wrapper = obj ;
if ( wrapper - > bridge ) {
ast_bridge_destroy ( wrapper - > bridge ) ;
}
}
AST_MUTEX_DEFINE_STATIC ( bridgewait_lock ) ;
static struct wait_bridge_wrapper * wait_bridge_wrapper_find_by_name ( const char * bridge_name )
{
return ao2_find ( wait_bridge_wrappers , bridge_name , OBJ_KEY ) ;
}
static int wait_bridge_hash_fn ( const void * obj , const int flags )
{
const struct wait_bridge_wrapper * entry ;
const char * key ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
case OBJ_KEY :
key = obj ;
return ast_str_hash ( key ) ;
case OBJ_POINTER :
entry = obj ;
return ast_str_hash ( entry - > name ) ;
default :
/* Hash can only work on something with a full key. */
ast_assert ( 0 ) ;
return 0 ;
}
}
static int wait_bridge_sort_fn ( const void * obj_left , const void * obj_right , const int flags )
{
const struct wait_bridge_wrapper * left = obj_left ;
const struct wait_bridge_wrapper * right = obj_right ;
const char * right_key = obj_right ;
int cmp ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
case OBJ_POINTER :
right_key = right - > name ;
/* Fall through */
case OBJ_KEY :
cmp = strcmp ( left - > name , right_key ) ;
break ;
case OBJ_PARTIAL_KEY :
cmp = strncmp ( left - > name , right_key , strlen ( right_key ) ) ;
break ;
default :
/* Sort can only work on something with a full or partial key. */
ast_assert ( 0 ) ;
cmp = 0 ;
break ;
}
return cmp ;
}
enum bridgewait_flags {
enum bridgewait_flags {
MUXFLAG_MOHCLASS = ( 1 < < 0 ) ,
MUXFLAG_MOHCLASS = ( 1 < < 0 ) ,
@ -169,6 +238,7 @@ static int apply_option_moh(struct ast_channel *chan, const char *class_arg)
static int apply_option_entertainment ( struct ast_channel * chan , const char * entertainment_arg )
static int apply_option_entertainment ( struct ast_channel * chan , const char * entertainment_arg )
{
{
char entertainment = entertainment_arg [ 0 ] ;
char entertainment = entertainment_arg [ 0 ] ;
switch ( entertainment ) {
switch ( entertainment ) {
case ' m ' :
case ' m ' :
return ast_channel_set_bridge_role_option ( chan , " holding_participant " , " idle_mode " , " musiconhold " ) ;
return ast_channel_set_bridge_role_option ( chan , " holding_participant " , " idle_mode " , " musiconhold " ) ;
@ -232,6 +302,98 @@ static int process_options(struct ast_channel *chan, struct ast_flags *flags, ch
return 0 ;
return 0 ;
}
}
/*!
* \ internal
* \ since 12.0 .0
* \ brief Allocate a new holding bridge wrapper with the given bridge name and bridge ID .
*
* \ param bridge_name name of the bridge wrapper
* \ param bridge the bridge being wrapped
*
* \ retval Pointer to the newly allocated holding bridge wrapper
* \ retval NULL if allocation failed . The bridge will be destroyed if this function fails .
*/
static struct wait_bridge_wrapper * wait_bridge_wrapper_alloc ( const char * bridge_name , struct ast_bridge * bridge )
{
struct wait_bridge_wrapper * bridge_wrapper ;
bridge_wrapper = ao2_alloc_options ( sizeof ( * bridge_wrapper ) + strlen ( bridge_name ) + 1 ,
wait_bridge_wrapper_destructor , AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( ! bridge_wrapper ) {
ast_bridge_destroy ( bridge ) ;
return NULL ;
}
strcpy ( bridge_wrapper - > name , bridge_name ) ;
bridge_wrapper - > bridge = bridge ;
if ( ! ao2_link ( wait_bridge_wrappers , bridge_wrapper ) ) {
ao2_cleanup ( bridge_wrapper ) ;
return NULL ;
}
return bridge_wrapper ;
}
static struct wait_bridge_wrapper * get_wait_bridge_wrapper ( const char * bridge_name )
{
struct wait_bridge_wrapper * wrapper ;
struct ast_bridge * bridge = NULL ;
SCOPED_AO2LOCK ( lock , wait_bridge_wrappers ) ;
if ( ( wrapper = wait_bridge_wrapper_find_by_name ( bridge_name ) ) ) {
return wrapper ;
}
bridge = ast_bridge_base_new ( AST_BRIDGE_CAPABILITY_HOLDING ,
AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED
| AST_BRIDGE_FLAG_DISSOLVE_EMPTY ) ;
if ( ! bridge ) {
return NULL ;
}
/* The bridge reference is unconditionally passed. */
return wait_bridge_wrapper_alloc ( bridge_name , bridge ) ;
}
/*!
* \ internal
* \ since 12.0 .0
* \ brief If we are down to the last reference of a wrapper and it ' s still contained within the list , remove it from the list .
*
* \ param wrapper reference to wait bridge wrapper being checked for list culling - will be cleared on exit
*/
static void wait_wrapper_removal ( struct wait_bridge_wrapper * wrapper )
{
if ( ! wrapper ) {
return ;
}
ao2_lock ( wait_bridge_wrappers ) ;
if ( ao2_ref ( wrapper , 0 ) = = 2 ) {
/* Either we have the only real reference or else wrapper isn't in the container anyway. */
ao2_unlink ( wait_bridge_wrappers , wrapper ) ;
}
ao2_unlock ( wait_bridge_wrappers ) ;
ao2_cleanup ( wrapper ) ;
}
/*!
* \ internal
* \ since 12.0 .0
* \ brief Application callback for the bridgewait application
*
* \ param chan channel running the application
* \ param data Arguments to the application
*
* \ retval 0 Ran successfully and the call didn ' t hang up
* \ retval - 1 Failed or the call was hung up by the time the channel exited the holding bridge
*/
static enum wait_bridge_roles validate_role ( const char * role )
static enum wait_bridge_roles validate_role ( const char * role )
{
{
if ( ! strcmp ( role , " participant " ) ) {
if ( ! strcmp ( role , " participant " ) ) {
@ -245,33 +407,28 @@ static enum wait_bridge_roles validate_role(const char *role)
static int bridgewait_exec ( struct ast_channel * chan , const char * data )
static int bridgewait_exec ( struct ast_channel * chan , const char * data )
{
{
char * bridge_name = DEFAULT_BRIDGE_NAME ;
struct ast_bridge_features chan_features ;
struct ast_bridge_features chan_features ;
struct ast_flags flags = { 0 } ;
struct ast_flags flags = { 0 } ;
char * parse ;
char * parse ;
int bridge_join_failed = 0 ;
enum wait_bridge_roles role = ROLE_PARTICIPANT ;
enum wait_bridge_roles role = ROLE_PARTICIPANT ;
char * opts [ OPT_ARG_ARRAY_SIZE ] = { NULL , } ;
char * opts [ OPT_ARG_ARRAY_SIZE ] = { NULL , } ;
AST_DECLARE_APP_ARGS ( args ,
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( name ) ;
AST_APP_ARG ( role ) ;
AST_APP_ARG ( role ) ;
AST_APP_ARG ( options ) ;
AST_APP_ARG ( options ) ;
AST_APP_ARG ( other ) ; /* Any remaining unused arguments */
AST_APP_ARG ( other ) ; /* Any remaining unused arguments */
) ;
) ;
ast_mutex_lock ( & bridgewait_lock ) ;
if ( ! holding_bridge ) {
holding_bridge = ast_bridge_base_new ( AST_BRIDGE_CAPABILITY_HOLDING ,
AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED ) ;
}
ast_mutex_unlock ( & bridgewait_lock ) ;
if ( ! holding_bridge ) {
ast_log ( LOG_ERROR , " Could not create holding bridge for '%s'. \n " , ast_channel_name ( chan ) ) ;
return - 1 ;
}
parse = ast_strdupa ( data ) ;
parse = ast_strdupa ( data ) ;
AST_STANDARD_APP_ARGS ( args , parse ) ;
AST_STANDARD_APP_ARGS ( args , parse ) ;
if ( ! ast_strlen_zero ( args . name ) ) {
bridge_name = args . name ;
}
if ( ! ast_strlen_zero ( args . role ) ) {
if ( ! ast_strlen_zero ( args . role ) ) {
role = validate_role ( args . role ) ;
role = validate_role ( args . role ) ;
if ( role = = ROLE_INVALID ) {
if ( role = = ROLE_INVALID ) {
@ -282,6 +439,8 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
if ( ast_bridge_features_init ( & chan_features ) ) {
if ( ast_bridge_features_init ( & chan_features ) ) {
ast_bridge_features_cleanup ( & chan_features ) ;
ast_bridge_features_cleanup ( & chan_features ) ;
ast_log ( LOG_ERROR , " '%s' failed to enter the waiting bridge - could not set up channel features \n " ,
ast_channel_name ( chan ) ) ;
return - 1 ;
return - 1 ;
}
}
@ -289,28 +448,66 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
ast_app_parse_options ( bridgewait_opts , & flags , opts , args . options ) ;
ast_app_parse_options ( bridgewait_opts , & flags , opts , args . options ) ;
}
}
/* Answer the channel if needed */
if ( ast_channel_state ( chan ) ! = AST_STATE_UP ) {
ast_answer ( chan ) ;
}
if ( process_options ( chan , & flags , opts , & chan_features , role ) ) {
if ( process_options ( chan , & flags , opts , & chan_features , role ) ) {
ast_bridge_features_cleanup ( & chan_features ) ;
ast_bridge_features_cleanup ( & chan_features ) ;
return - 1 ;
return - 1 ;
}
}
ast_bridge_join ( holding_bridge , chan , NULL , & chan_features , NULL , 0 ) ;
for ( ; ; ) {
RAII_VAR ( struct wait_bridge_wrapper * , bridge_wrapper , get_wait_bridge_wrapper ( bridge_name ) , wait_wrapper_removal ) ;
if ( ! bridge_wrapper ) {
ast_log ( LOG_WARNING , " Failed to find or create waiting bridge '%s' for '%s'. \n " , bridge_name , ast_channel_name ( chan ) ) ;
bridge_join_failed = 1 ;
break ;
}
ast_verb ( 3 , " %s is entering waiting bridge %s:%s \n " , ast_channel_name ( chan ) , bridge_name , bridge_wrapper - > bridge - > uniqueid ) ;
if ( ast_bridge_join ( bridge_wrapper - > bridge , chan , NULL , & chan_features , NULL , 0 ) ) {
/* It's possible for a holding bridge to vanish out from under us since we can't lock it.
* Unlink the wrapper and then loop if the bridge we try to enter is dissolved . */
ast_verb ( 3 , " Waiting bridge '%s:%s' is no longer joinable. Creating new bridge and trying again. \n " ,
bridge_name , bridge_wrapper - > bridge - > uniqueid ) ;
ao2_unlink ( wait_bridge_wrappers , bridge_wrapper ) ;
continue ;
}
break ;
}
ast_bridge_features_cleanup ( & chan_features ) ;
ast_bridge_features_cleanup ( & chan_features ) ;
if ( bridge_join_failed ) {
return - 1 ;
}
return ast_check_hangup_locked ( chan ) ? - 1 : 0 ;
return ast_check_hangup_locked ( chan ) ? - 1 : 0 ;
}
}
static int unload_module ( void )
static int unload_module ( void )
{
{
ao2_cleanup ( holding_bridge ) ;
ao2_cleanup ( wait_bridge_wrappers ) ;
holding_bridge = NULL ;
return ast_unregister_application ( app ) ;
return ast_unregister_application ( APP_NAME ) ;
}
}
static int load_module ( void )
static int load_module ( void )
{
{
return ast_register_application_xml ( app , bridgewait_exec ) ;
wait_bridge_wrappers = ao2_container_alloc_hash (
AO2_ALLOC_OPT_LOCK_MUTEX , AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT ,
37 , wait_bridge_hash_fn , wait_bridge_sort_fn , NULL ) ;
if ( ! wait_bridge_wrappers ) {
return - 1 ;
}
return ast_register_application_xml ( APP_NAME , bridgewait_exec ) ;
}
}
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " Place the channel into a holding bridge application " ) ;
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " Place the channel into a holding bridge application " ) ;