2010-04-09 15:31:32 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2010, Digium, Inc.
*
* Mark Michelson <mmichelson@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
* \brief Call Completion Supplementary Services implementation
* \author Mark Michelson <mmichelson@digium.com>
*/
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
# include "asterisk/astobj2.h"
# include "asterisk/strings.h"
# include "asterisk/ccss.h"
# include "asterisk/channel.h"
# include "asterisk/pbx.h"
# include "asterisk/utils.h"
# include "asterisk/taskprocessor.h"
# include "asterisk/event.h"
# include "asterisk/module.h"
# include "asterisk/app.h"
# include "asterisk/cli.h"
# include "asterisk/manager.h"
# include "asterisk/causes.h"
/*** DOCUMENTATION
<application name="CallCompletionRequest" language="en_US">
<synopsis>
Request call completion service for previous call
</synopsis>
<syntax />
<description>
<para>Request call completion service for a previously failed
call attempt.</para>
</description>
</application>
<application name="CallCompletionCancel" language="en_US">
<synopsis>
Cancel call completion service
</synopsis>
<syntax />
<description>
<para>Cancel a Call Completion Request.</para>
</description>
</application>
***/
/* These are some file-scoped variables. It would be
* nice to define them closer to their first usage, but since
* they are used in many places throughout the file, defining
* them here at the top is easiest.
*/
/*!
* The sched_thread ID used for all generic CC timeouts
*/
static struct ast_sched_thread * cc_sched_thread ;
/*!
* Counter used to create core IDs for CC calls. Each new
* core ID is created by atomically adding 1 to the core_id_counter
*/
static int core_id_counter ;
/*!
* Taskprocessor from which all CC agent and monitor callbacks
* are called.
*/
static struct ast_taskprocessor * cc_core_taskprocessor ;
/*!
* Name printed on all CC log messages.
*/
static const char * CC_LOGGER_LEVEL_NAME = " CC " ;
/*!
* Logger level registered by the CC core.
*/
static int cc_logger_level ;
/*!
* Parsed configuration value for cc_max_requests
*/
static unsigned int global_cc_max_requests ;
/*!
* The current number of CC requests in the system
*/
static int cc_request_count ;
2010-04-27 19:52:18 +00:00
static inline void * cc_ref ( void * obj , const char * debug )
{
ao2_t_ref ( obj , + 1 , debug ) ;
return obj ;
}
static inline void * cc_unref ( void * obj , const char * debug )
{
ao2_t_ref ( obj , - 1 , debug ) ;
return NULL ;
}
2010-04-09 15:31:32 +00:00
/*!
* \since 1.8
* \internal
* \brief A structure for holding the configuration parameters
* relating to CCSS
*/
struct ast_cc_config_params {
enum ast_cc_agent_policies cc_agent_policy ;
enum ast_cc_monitor_policies cc_monitor_policy ;
unsigned int cc_offer_timer ;
unsigned int ccnr_available_timer ;
unsigned int ccbs_available_timer ;
unsigned int cc_recall_timer ;
unsigned int cc_max_agents ;
unsigned int cc_max_monitors ;
char cc_callback_macro [ AST_MAX_EXTENSION ] ;
char cc_agent_dialstring [ AST_MAX_EXTENSION ] ;
} ;
/*!
* \since 1.8
* \brief The states used in the CCSS core state machine
*
* For more information, see doc/CCSS_architecture.pdf
*/
enum cc_state {
/*! Entered when it is determined that CCSS may be used for the call */
CC_AVAILABLE ,
/*! Entered when a CCSS agent has offered CCSS to a caller */
CC_CALLER_OFFERED ,
/*! Entered when a CCSS agent confirms that a caller has
* requested CCSS */
CC_CALLER_REQUESTED ,
/*! Entered when a CCSS monitor confirms acknowledgment of an
* outbound CCSS request */
CC_ACTIVE ,
/*! Entered when a CCSS monitor alerts the core that the called party
* has become available */
CC_CALLEE_READY ,
/*! Entered when a CCSS agent alerts the core that the calling party
* may not be recalled because he is unavailable
*/
CC_CALLER_BUSY ,
/*! Entered when a CCSS agent alerts the core that the calling party
* is attempting to recall the called party
*/
CC_RECALLING ,
/*! Entered when an application alerts the core that the calling party's
* recall attempt has had a call progress response indicated
*/
CC_COMPLETE ,
/*! Entered any time that something goes wrong during the process, thus
* resulting in the failure of the attempted CCSS transaction. Note also
* that cancellations of CC are treated as failures.
*/
CC_FAILED ,
} ;
/*!
* \brief The payload for an AST_CONTROL_CC frame
*
* \details
* This contains all the necessary data regarding
* a called device so that the CC core will be able
* to allocate the proper monitoring resources.
*/
struct cc_control_payload {
/*!
* \brief The type of monitor to allocate.
*
* \details
* The type of monitor to allocate. This is a string which corresponds
* to a set of monitor callbacks registered. Examples include "generic"
* and "SIP"
*
* \note This really should be an array of characters in case this payload
* is sent accross an IAX2 link. However, this would not make too much sense
* given this type may not be recognized by the other end.
* Protection may be necessary to prevent it from being transmitted.
*
* In addition the following other problems are also possible:
* 1) Endian issues with the integers/enums stored in the config_params.
* 2) Alignment padding issues for the element types.
*/
const char * monitor_type ;
/*!
* \brief Private data allocated by the callee
*
* \details
* All channel drivers that monitor endpoints will need to allocate
* data that is not usable by the CC core. In most cases, some or all
* of this data is allocated at the time that the channel driver offers
* CC to the caller. There are many opportunities for failures to occur
* between when a channel driver offers CC and when a monitor is actually
* allocated to watch the endpoint. For this reason, the channel driver
* must give the core a pointer to the private data that was allocated so
* that the core can call back into the channel driver to destroy it if
* a failure occurs. If no private data has been allocated at the time that
* CC is offered, then it is perfectly acceptable to pass NULL for this
* field.
*/
void * private_data ;
/*!
* \brief Service offered by the endpoint
*
* \details
* This indicates the type of call completion service offered by the
* endpoint. This data is not crucial to the machinations of the CC core,
* but it is helpful for debugging purposes.
*/
enum ast_cc_service_type service ;
/*!
* \brief Configuration parameters used by this endpoint
*
* \details
* Each time an endpoint offers call completion, it must provide its call
* completion configuration parameters. This is because settings may be different
* depending on the circumstances.
*/
struct ast_cc_config_params config_params ;
/*!
* \brief ID of parent extension
*
* \details
* This is the only datum that the CC core derives on its own and is not
* provided by the offerer of CC. This provides the core with information on
* which extension monitor is the most immediate parent of this device.
*/
int parent_interface_id ;
/*!
* \brief Name of device to be monitored
*
* \details
* The device name by which this monitored endpoint will be referred in the
* CC core. It is highly recommended that this device name is derived by using
* the function ast_channel_get_device_name.
*/
char device_name [ AST_CHANNEL_NAME ] ;
/*!
* \brief Recall dialstring
*
* \details
* Certain channel drivers (DAHDI in particular) will require that a special
* dialstring be used to indicate that the outgoing call is to interpreted as
* a CC recall. If the channel driver has such a requirement, then this is
* where that special recall dialstring is placed. If no special dialstring
* is to be used, then the channel driver must provide the original dialstring
* used to call this endpoint.
*/
char dialstring [ AST_CHANNEL_NAME ] ;
} ;
/*!
* \brief The "tree" of interfaces that is dialed.
*
* \details
* Though this is a linked list, it is logically treated
* as a tree of monitors. Each monitor has an id and a parent_id
* associated with it. The id is a unique ID for that monitor, and
* the parent_id is the unique ID of the monitor's parent in the
* tree. The tree is structured such that all of a parent's children
* will appear after the parent in the tree. However, it cannot be
* guaranteed exactly where after the parent the children are.
*
* The tree is reference counted since several threads may need
* to use it, and it may last beyond the lifetime of a single
* thread.
*/
AST_LIST_HEAD ( cc_monitor_tree , ast_cc_monitor ) ;
static const int CC_CORE_INSTANCES_BUCKETS = 17 ;
static struct ao2_container * cc_core_instances ;
struct cc_core_instance {
/*!
* Unique identifier for this instance of the CC core.
*/
int core_id ;
/*!
* The current state for this instance of the CC core.
*/
enum cc_state current_state ;
/*!
* The CC agent in use for this call
*/
struct ast_cc_agent * agent ;
/*!
* Reference to the monitor tree formed during the initial call
*/
struct cc_monitor_tree * monitors ;
} ;
/*!
* \internal
* \brief Request that the core change states
* \param state The state to which we wish to change
* \param core_id The unique identifier for this instance of the CCSS core state machine
* \param debug Optional message explaining the reason for the state change
* \param ap varargs list
* \retval 0 State change successfully queued
* \retval -1 Unable to queue state change request
*/
static int __attribute__ ( ( format ( printf , 3 , 0 ) ) ) cc_request_state_change ( enum cc_state state , const int core_id , const char * debug , va_list ap ) ;
/*!
* \internal
* \brief create a new instance of the CC core and an agent for the calling channel
*
* This function will check to make sure that the incoming channel
* is allowed to request CC by making sure that the incoming channel
* has not exceeded its maximum number of allowed agents.
*
* Should that check pass, the core instance is created, and then the
* agent for the channel.
*
* \param caller_chan The incoming channel for this particular call
* \param called_tree A reference to the tree of called devices. The agent
* will gain a reference to this tree as well
* \param core_id The core_id that this core_instance will assume
* \retval NULL Failed to create the core instance either due to memory allocation
* errors or due to the agent count for the caller being too high
* \retval non-NULL A reference to the newly created cc_core_instance
*/
static struct cc_core_instance * cc_core_init_instance ( struct ast_channel * caller_chan ,
struct cc_monitor_tree * called_tree , const int core_id , struct cc_control_payload * cc_data ) ;
static const struct {
enum ast_cc_service_type service ;
const char * service_string ;
} cc_service_to_string_map [ ] = {
{ AST_CC_NONE , " NONE " } ,
{ AST_CC_CCBS , " CCBS " } ,
{ AST_CC_CCNR , " CCNR " } ,
{ AST_CC_CCNL , " CCNL " } ,
} ;
static const struct {
enum cc_state state ;
const char * state_string ;
} cc_state_to_string_map [ ] = {
{ CC_AVAILABLE , " CC is available " } ,
{ CC_CALLER_OFFERED , " CC offered to caller " } ,
{ CC_CALLER_REQUESTED , " CC requested by caller " } ,
{ CC_ACTIVE , " CC accepted by callee " } ,
{ CC_CALLEE_READY , " Callee has become available " } ,
{ CC_CALLER_BUSY , " Callee was ready, but caller is now unavailable " } ,
{ CC_RECALLING , " Caller is attempting to recall " } ,
{ CC_COMPLETE , " Recall complete " } ,
{ CC_FAILED , " CC has failed " } ,
} ;
static const char * cc_state_to_string ( enum cc_state state )
{
return cc_state_to_string_map [ state ] . state_string ;
}
static const char * cc_service_to_string ( enum ast_cc_service_type service )
{
return cc_service_to_string_map [ service ] . service_string ;
}
static int cc_core_instance_hash_fn ( const void * obj , const int flags )
{
const struct cc_core_instance * core_instance = obj ;
return core_instance - > core_id ;
}
static int cc_core_instance_cmp_fn ( void * obj , void * arg , int flags )
{
struct cc_core_instance * core_instance1 = obj ;
struct cc_core_instance * core_instance2 = arg ;
return core_instance1 - > core_id = = core_instance2 - > core_id ? CMP_MATCH | CMP_STOP : 0 ;
}
static struct cc_core_instance * find_cc_core_instance ( const int core_id )
{
struct cc_core_instance finder = { . core_id = core_id , } ;
return ao2_t_find ( cc_core_instances , & finder , OBJ_POINTER , " Finding a core_instance " ) ;
}
struct cc_callback_helper {
ao2_callback_fn * function ;
void * args ;
const char * type ;
} ;
static int cc_agent_callback_helper ( void * obj , void * args , int flags )
{
struct cc_core_instance * core_instance = obj ;
struct cc_callback_helper * helper = args ;
if ( strcmp ( core_instance - > agent - > callbacks - > type , helper - > type ) ) {
return 0 ;
}
return helper - > function ( core_instance - > agent , helper - > args , flags ) ;
}
struct ast_cc_agent * ast_cc_agent_callback ( int flags , ao2_callback_fn * function , void * args , const char * const type )
{
struct cc_callback_helper helper = { . function = function , . args = args , . type = type } ;
struct cc_core_instance * core_instance ;
if ( ( core_instance = ao2_t_callback ( cc_core_instances , flags , cc_agent_callback_helper , & helper ,
" Calling provided agent callback function " ) ) ) {
struct ast_cc_agent * agent = cc_ref ( core_instance - > agent , " An outside entity needs the agent " ) ;
cc_unref ( core_instance , " agent callback done with the core_instance " ) ;
return agent ;
}
return NULL ;
}
enum match_flags {
/* Only match agents that have not yet
* made a CC request
*/
MATCH_NO_REQUEST = ( 1 < < 0 ) ,
/* Only match agents that have made
* a CC request
*/
MATCH_REQUEST = ( 1 < < 1 ) ,
} ;
/* ao2_callbacks for cc_core_instances */
/*!
* \internal
* \brief find a core instance based on its agent
*
* The match flags tell whether we wish to find core instances
* that have a monitor or core instances that do not. Core instances
* with no monitor are core instances for which a caller has not yet
* requested CC. Core instances with a monitor are ones for which the
* caller has requested CC.
*/
static int match_agent ( void * obj , void * arg , void * data , int flags )
{
struct cc_core_instance * core_instance = obj ;
const char * name = arg ;
unsigned long match_flags = * ( unsigned long * ) data ;
int possible_match = 0 ;
if ( ( match_flags & MATCH_NO_REQUEST ) & & core_instance - > current_state < CC_CALLER_REQUESTED ) {
possible_match = 1 ;
}
if ( ( match_flags & MATCH_REQUEST ) & & core_instance - > current_state > = CC_CALLER_REQUESTED ) {
possible_match = 1 ;
}
if ( ! possible_match ) {
return 0 ;
}
if ( ! strcmp ( core_instance - > agent - > device_name , name ) ) {
return CMP_MATCH | CMP_STOP ;
}
return 0 ;
}
struct count_agents_cb_data {
int count ;
int core_id_exception ;
} ;
/*!
* \internal
* \brief Count the number of agents a specific interface is using
*
* We're only concerned with the number of agents that have requested
* CC, so we restrict our search to core instances which have a non-NULL
* monitor pointer
*/
static int count_agents_cb ( void * obj , void * arg , void * data , int flags )
{
struct cc_core_instance * core_instance = obj ;
const char * name = arg ;
struct count_agents_cb_data * cb_data = data ;
if ( cb_data - > core_id_exception = = core_instance - > core_id ) {
ast_log_dynamic_level ( cc_logger_level , " Found agent with core_id %d but not counting it toward total \n " , core_instance - > core_id ) ;
return 0 ;
}
if ( core_instance - > current_state > = CC_CALLER_REQUESTED & & ! strcmp ( core_instance - > agent - > device_name , name ) ) {
cb_data - > count + + ;
}
return 0 ;
}
2010-08-12 22:10:49 +00:00
# define CC_OFFER_TIMER_DEFAULT 20 /* Seconds */
# define CCNR_AVAILABLE_TIMER_DEFAULT 7200 /* Seconds */
# define CCBS_AVAILABLE_TIMER_DEFAULT 4800 /* Seconds */
# define CC_RECALL_TIMER_DEFAULT 20 /* Seconds */
# define CC_MAX_AGENTS_DEFAULT 5
# define CC_MAX_MONITORS_DEFAULT 5
# define GLOBAL_CC_MAX_REQUESTS_DEFAULT 20
static const struct ast_cc_config_params cc_default_params = {
. cc_agent_policy = AST_CC_AGENT_NEVER ,
. cc_monitor_policy = AST_CC_MONITOR_NEVER ,
. cc_offer_timer = CC_OFFER_TIMER_DEFAULT ,
. ccnr_available_timer = CCNR_AVAILABLE_TIMER_DEFAULT ,
. ccbs_available_timer = CCBS_AVAILABLE_TIMER_DEFAULT ,
. cc_recall_timer = CC_RECALL_TIMER_DEFAULT ,
. cc_max_agents = CC_MAX_AGENTS_DEFAULT ,
. cc_max_monitors = CC_MAX_MONITORS_DEFAULT ,
. cc_callback_macro = " " ,
. cc_agent_dialstring = " " ,
} ;
void ast_cc_default_config_params ( struct ast_cc_config_params * params )
{
* params = cc_default_params ;
}
2010-04-09 15:31:32 +00:00
struct ast_cc_config_params * __ast_cc_config_params_init ( const char * file , int line , const char * function )
{
# if defined(__AST_DEBUG_MALLOC)
2010-08-12 22:10:49 +00:00
struct ast_cc_config_params * params = __ast_malloc ( sizeof ( * params ) , file , line , function ) ;
2010-04-09 15:31:32 +00:00
# else
2010-08-12 22:10:49 +00:00
struct ast_cc_config_params * params = ast_malloc ( sizeof ( * params ) ) ;
2010-04-09 15:31:32 +00:00
# endif
if ( ! params ) {
return NULL ;
}
2010-08-12 22:10:49 +00:00
ast_cc_default_config_params ( params ) ;
2010-04-09 15:31:32 +00:00
return params ;
}
void ast_cc_config_params_destroy ( struct ast_cc_config_params * params )
{
ast_free ( params ) ;
}
static enum ast_cc_agent_policies str_to_agent_policy ( const char * const value )
{
if ( ! strcasecmp ( value , " never " ) ) {
return AST_CC_AGENT_NEVER ;
} else if ( ! strcasecmp ( value , " native " ) ) {
return AST_CC_AGENT_NATIVE ;
} else if ( ! strcasecmp ( value , " generic " ) ) {
return AST_CC_AGENT_GENERIC ;
} else {
ast_log ( LOG_WARNING , " %s is an invalid value for cc_agent_policy. Switching to 'never' \n " , value ) ;
return AST_CC_AGENT_NEVER ;
}
}
static enum ast_cc_monitor_policies str_to_monitor_policy ( const char * const value )
{
if ( ! strcasecmp ( value , " never " ) ) {
return AST_CC_MONITOR_NEVER ;
} else if ( ! strcasecmp ( value , " native " ) ) {
return AST_CC_MONITOR_NATIVE ;
} else if ( ! strcasecmp ( value , " generic " ) ) {
return AST_CC_MONITOR_GENERIC ;
} else if ( ! strcasecmp ( value , " always " ) ) {
return AST_CC_MONITOR_ALWAYS ;
} else {
ast_log ( LOG_WARNING , " %s is an invalid value for cc_monitor_policy. Switching to 'never' \n " , value ) ;
return AST_CC_MONITOR_NEVER ;
}
}
static const char * agent_policy_to_str ( enum ast_cc_agent_policies policy )
{
switch ( policy ) {
case AST_CC_AGENT_NEVER :
return " never " ;
case AST_CC_AGENT_NATIVE :
return " native " ;
case AST_CC_AGENT_GENERIC :
return " generic " ;
default :
/* This should never happen... */
return " " ;
}
}
static const char * monitor_policy_to_str ( enum ast_cc_monitor_policies policy )
{
switch ( policy ) {
case AST_CC_MONITOR_NEVER :
return " never " ;
case AST_CC_MONITOR_NATIVE :
return " native " ;
case AST_CC_MONITOR_GENERIC :
return " generic " ;
case AST_CC_MONITOR_ALWAYS :
return " always " ;
default :
/* This should never happen... */
return " " ;
}
}
int ast_cc_get_param ( struct ast_cc_config_params * params , const char * const name ,
char * buf , size_t buf_len )
{
const char * value = NULL ;
if ( ! strcasecmp ( name , " cc_callback_macro " ) ) {
value = ast_get_cc_callback_macro ( params ) ;
} else if ( ! strcasecmp ( name , " cc_agent_policy " ) ) {
value = agent_policy_to_str ( ast_get_cc_agent_policy ( params ) ) ;
} else if ( ! strcasecmp ( name , " cc_monitor_policy " ) ) {
value = monitor_policy_to_str ( ast_get_cc_monitor_policy ( params ) ) ;
} else if ( ! strcasecmp ( name , " cc_agent_dialstring " ) ) {
value = ast_get_cc_agent_dialstring ( params ) ;
}
if ( ! ast_strlen_zero ( value ) ) {
ast_copy_string ( buf , value , buf_len ) ;
return 0 ;
}
/* The rest of these are all ints of some sort and require some
* snprintf-itude
*/
if ( ! strcasecmp ( name , " cc_offer_timer " ) ) {
snprintf ( buf , buf_len , " %u " , ast_get_cc_offer_timer ( params ) ) ;
} else if ( ! strcasecmp ( name , " ccnr_available_timer " ) ) {
snprintf ( buf , buf_len , " %u " , ast_get_ccnr_available_timer ( params ) ) ;
} else if ( ! strcasecmp ( name , " ccbs_available_timer " ) ) {
snprintf ( buf , buf_len , " %u " , ast_get_ccbs_available_timer ( params ) ) ;
} else if ( ! strcasecmp ( name , " cc_max_agents " ) ) {
snprintf ( buf , buf_len , " %u " , ast_get_cc_max_agents ( params ) ) ;
} else if ( ! strcasecmp ( name , " cc_max_monitors " ) ) {
snprintf ( buf , buf_len , " %u " , ast_get_cc_max_monitors ( params ) ) ;
} else if ( ! strcasecmp ( name , " cc_recall_timer " ) ) {
snprintf ( buf , buf_len , " %u " , ast_get_cc_recall_timer ( params ) ) ;
} else {
ast_log ( LOG_WARNING , " %s is not a valid CC parameter. Ignoring. \n " , name ) ;
return - 1 ;
}
return 0 ;
}
int ast_cc_set_param ( struct ast_cc_config_params * params , const char * const name ,
const char * const value )
{
unsigned int value_as_uint ;
if ( ! strcasecmp ( name , " cc_agent_policy " ) ) {
return ast_set_cc_agent_policy ( params , str_to_agent_policy ( value ) ) ;
} else if ( ! strcasecmp ( name , " cc_monitor_policy " ) ) {
return ast_set_cc_monitor_policy ( params , str_to_monitor_policy ( value ) ) ;
} else if ( ! strcasecmp ( name , " cc_agent_dialstring " ) ) {
ast_set_cc_agent_dialstring ( params , value ) ;
} else if ( ! strcasecmp ( name , " cc_callback_macro " ) ) {
ast_set_cc_callback_macro ( params , value ) ;
return 0 ;
}
if ( ! sscanf ( value , " %30u " , & value_as_uint ) = = 1 ) {
return - 1 ;
}
if ( ! strcasecmp ( name , " cc_offer_timer " ) ) {
ast_set_cc_offer_timer ( params , value_as_uint ) ;
} else if ( ! strcasecmp ( name , " ccnr_available_timer " ) ) {
ast_set_ccnr_available_timer ( params , value_as_uint ) ;
} else if ( ! strcasecmp ( name , " ccbs_available_timer " ) ) {
ast_set_ccbs_available_timer ( params , value_as_uint ) ;
} else if ( ! strcasecmp ( name , " cc_max_agents " ) ) {
ast_set_cc_max_agents ( params , value_as_uint ) ;
} else if ( ! strcasecmp ( name , " cc_max_monitors " ) ) {
ast_set_cc_max_monitors ( params , value_as_uint ) ;
} else if ( ! strcasecmp ( name , " cc_recall_timer " ) ) {
ast_set_cc_recall_timer ( params , value_as_uint ) ;
} else {
ast_log ( LOG_WARNING , " %s is not a valid CC parameter. Ignoring. \n " , name ) ;
return - 1 ;
}
return 0 ;
}
int ast_cc_is_config_param ( const char * const name )
{
return ( ! strcasecmp ( name , " cc_agent_policy " ) | |
! strcasecmp ( name , " cc_monitor_policy " ) | |
! strcasecmp ( name , " cc_offer_timer " ) | |
! strcasecmp ( name , " ccnr_available_timer " ) | |
! strcasecmp ( name , " ccbs_available_timer " ) | |
! strcasecmp ( name , " cc_max_agents " ) | |
! strcasecmp ( name , " cc_max_monitors " ) | |
! strcasecmp ( name , " cc_callback_macro " ) | |
! strcasecmp ( name , " cc_agent_dialstring " ) | |
! strcasecmp ( name , " cc_recall_timer " ) ) ;
}
void ast_cc_copy_config_params ( struct ast_cc_config_params * dest , const struct ast_cc_config_params * src )
{
* dest = * src ;
}
enum ast_cc_agent_policies ast_get_cc_agent_policy ( struct ast_cc_config_params * config )
{
return config - > cc_agent_policy ;
}
int ast_set_cc_agent_policy ( struct ast_cc_config_params * config , enum ast_cc_agent_policies value )
{
/* Screw C and its weak type checking for making me have to do this
* validation at runtime.
*/
if ( value < AST_CC_AGENT_NEVER | | value > AST_CC_AGENT_GENERIC ) {
return - 1 ;
}
config - > cc_agent_policy = value ;
return 0 ;
}
enum ast_cc_monitor_policies ast_get_cc_monitor_policy ( struct ast_cc_config_params * config )
{
return config - > cc_monitor_policy ;
}
int ast_set_cc_monitor_policy ( struct ast_cc_config_params * config , enum ast_cc_monitor_policies value )
{
/* Screw C and its weak type checking for making me have to do this
* validation at runtime.
*/
if ( value < AST_CC_MONITOR_NEVER | | value > AST_CC_MONITOR_ALWAYS ) {
return - 1 ;
}
config - > cc_monitor_policy = value ;
return 0 ;
}
unsigned int ast_get_cc_offer_timer ( struct ast_cc_config_params * config )
{
return config - > cc_offer_timer ;
}
void ast_set_cc_offer_timer ( struct ast_cc_config_params * config , unsigned int value )
{
/* 0 is an unreasonable value for any timer. Stick with the default */
if ( value = = 0 ) {
ast_log ( LOG_WARNING , " 0 is an invalid value for cc_offer_timer. Retaining value as %u \n " , config - > cc_offer_timer ) ;
return ;
}
config - > cc_offer_timer = value ;
}
unsigned int ast_get_ccnr_available_timer ( struct ast_cc_config_params * config )
{
return config - > ccnr_available_timer ;
}
void ast_set_ccnr_available_timer ( struct ast_cc_config_params * config , unsigned int value )
{
/* 0 is an unreasonable value for any timer. Stick with the default */
if ( value = = 0 ) {
ast_log ( LOG_WARNING , " 0 is an invalid value for ccnr_available_timer. Retaining value as %u \n " , config - > ccnr_available_timer ) ;
return ;
}
config - > ccnr_available_timer = value ;
}
unsigned int ast_get_cc_recall_timer ( struct ast_cc_config_params * config )
{
return config - > cc_recall_timer ;
}
void ast_set_cc_recall_timer ( struct ast_cc_config_params * config , unsigned int value )
{
/* 0 is an unreasonable value for any timer. Stick with the default */
if ( value = = 0 ) {
ast_log ( LOG_WARNING , " 0 is an invalid value for ccnr_available_timer. Retaining value as %u \n " , config - > cc_recall_timer ) ;
return ;
}
config - > cc_recall_timer = value ;
}
unsigned int ast_get_ccbs_available_timer ( struct ast_cc_config_params * config )
{
return config - > ccbs_available_timer ;
}
void ast_set_ccbs_available_timer ( struct ast_cc_config_params * config , unsigned int value )
{
/* 0 is an unreasonable value for any timer. Stick with the default */
if ( value = = 0 ) {
ast_log ( LOG_WARNING , " 0 is an invalid value for ccbs_available_timer. Retaining value as %u \n " , config - > ccbs_available_timer ) ;
return ;
}
config - > ccbs_available_timer = value ;
}
const char * ast_get_cc_agent_dialstring ( struct ast_cc_config_params * config )
{
return config - > cc_agent_dialstring ;
}
void ast_set_cc_agent_dialstring ( struct ast_cc_config_params * config , const char * const value )
{
if ( ast_strlen_zero ( value ) ) {
config - > cc_agent_dialstring [ 0 ] = ' \0 ' ;
} else {
ast_copy_string ( config - > cc_agent_dialstring , value , sizeof ( config - > cc_agent_dialstring ) ) ;
}
}
unsigned int ast_get_cc_max_agents ( struct ast_cc_config_params * config )
{
return config - > cc_max_agents ;
}
void ast_set_cc_max_agents ( struct ast_cc_config_params * config , unsigned int value )
{
config - > cc_max_agents = value ;
}
unsigned int ast_get_cc_max_monitors ( struct ast_cc_config_params * config )
{
return config - > cc_max_monitors ;
}
void ast_set_cc_max_monitors ( struct ast_cc_config_params * config , unsigned int value )
{
config - > cc_max_monitors = value ;
}
const char * ast_get_cc_callback_macro ( struct ast_cc_config_params * config )
{
return config - > cc_callback_macro ;
}
void ast_set_cc_callback_macro ( struct ast_cc_config_params * config , const char * const value )
{
if ( ast_strlen_zero ( value ) ) {
config - > cc_callback_macro [ 0 ] = ' \0 ' ;
} else {
ast_copy_string ( config - > cc_callback_macro , value , sizeof ( config - > cc_callback_macro ) ) ;
}
}
struct cc_monitor_backend {
AST_LIST_ENTRY ( cc_monitor_backend ) next ;
const struct ast_cc_monitor_callbacks * callbacks ;
} ;
AST_RWLIST_HEAD_STATIC ( cc_monitor_backends , cc_monitor_backend ) ;
int ast_cc_monitor_register ( const struct ast_cc_monitor_callbacks * callbacks )
{
struct cc_monitor_backend * backend = ast_calloc ( 1 , sizeof ( * backend ) ) ;
if ( ! backend ) {
return - 1 ;
}
backend - > callbacks = callbacks ;
AST_RWLIST_WRLOCK ( & cc_monitor_backends ) ;
AST_RWLIST_INSERT_TAIL ( & cc_monitor_backends , backend , next ) ;
AST_RWLIST_UNLOCK ( & cc_monitor_backends ) ;
return 0 ;
}
static const struct ast_cc_monitor_callbacks * find_monitor_callbacks ( const char * const type )
{
struct cc_monitor_backend * backend ;
const struct ast_cc_monitor_callbacks * callbacks = NULL ;
AST_RWLIST_RDLOCK ( & cc_monitor_backends ) ;
AST_RWLIST_TRAVERSE ( & cc_monitor_backends , backend , next ) {
if ( ! strcmp ( backend - > callbacks - > type , type ) ) {
ast_log_dynamic_level ( cc_logger_level , " Returning monitor backend %s \n " , backend - > callbacks - > type ) ;
callbacks = backend - > callbacks ;
break ;
}
}
AST_RWLIST_UNLOCK ( & cc_monitor_backends ) ;
return callbacks ;
}
void ast_cc_monitor_unregister ( const struct ast_cc_monitor_callbacks * callbacks )
{
struct cc_monitor_backend * backend ;
AST_RWLIST_WRLOCK ( & cc_monitor_backends ) ;
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & cc_monitor_backends , backend , next ) {
if ( backend - > callbacks = = callbacks ) {
AST_RWLIST_REMOVE_CURRENT ( next ) ;
ast_free ( backend ) ;
break ;
}
}
AST_RWLIST_TRAVERSE_SAFE_END ;
AST_RWLIST_UNLOCK ( & cc_monitor_backends ) ;
}
struct cc_agent_backend {
AST_LIST_ENTRY ( cc_agent_backend ) next ;
const struct ast_cc_agent_callbacks * callbacks ;
} ;
AST_RWLIST_HEAD_STATIC ( cc_agent_backends , cc_agent_backend ) ;
int ast_cc_agent_register ( const struct ast_cc_agent_callbacks * callbacks )
{
struct cc_agent_backend * backend = ast_calloc ( 1 , sizeof ( * backend ) ) ;
if ( ! backend ) {
return - 1 ;
}
backend - > callbacks = callbacks ;
AST_RWLIST_WRLOCK ( & cc_agent_backends ) ;
AST_RWLIST_INSERT_TAIL ( & cc_agent_backends , backend , next ) ;
AST_RWLIST_UNLOCK ( & cc_agent_backends ) ;
return 0 ;
}
void ast_cc_agent_unregister ( const struct ast_cc_agent_callbacks * callbacks )
{
struct cc_agent_backend * backend ;
AST_RWLIST_WRLOCK ( & cc_agent_backends ) ;
AST_RWLIST_TRAVERSE_SAFE_BEGIN ( & cc_agent_backends , backend , next ) {
if ( backend - > callbacks = = callbacks ) {
AST_RWLIST_REMOVE_CURRENT ( next ) ;
ast_free ( backend ) ;
break ;
}
}
AST_RWLIST_TRAVERSE_SAFE_END ;
AST_RWLIST_UNLOCK ( & cc_agent_backends ) ;
}
static const struct ast_cc_agent_callbacks * find_agent_callbacks ( struct ast_channel * chan )
{
struct cc_agent_backend * backend ;
const struct ast_cc_agent_callbacks * callbacks = NULL ;
struct ast_cc_config_params * cc_params ;
char type [ 32 ] ;
cc_params = ast_channel_get_cc_config_params ( chan ) ;
if ( ! cc_params ) {
return NULL ;
}
switch ( ast_get_cc_agent_policy ( cc_params ) ) {
case AST_CC_AGENT_GENERIC :
ast_copy_string ( type , " generic " , sizeof ( type ) ) ;
break ;
case AST_CC_AGENT_NATIVE :
ast_channel_get_cc_agent_type ( chan , type , sizeof ( type ) ) ;
break ;
default :
ast_log_dynamic_level ( cc_logger_level , " Not returning agent callbacks since this channel is configured not to have a CC agent \n " ) ;
return NULL ;
}
AST_RWLIST_RDLOCK ( & cc_agent_backends ) ;
AST_RWLIST_TRAVERSE ( & cc_agent_backends , backend , next ) {
if ( ! strcmp ( backend - > callbacks - > type , type ) ) {
ast_log_dynamic_level ( cc_logger_level , " Returning agent backend %s \n " , backend - > callbacks - > type ) ;
callbacks = backend - > callbacks ;
break ;
}
}
AST_RWLIST_UNLOCK ( & cc_agent_backends ) ;
return callbacks ;
}
static int cc_generic_monitor_request_cc ( struct ast_cc_monitor * monitor , int * available_timer_id ) ;
static int cc_generic_monitor_suspend ( struct ast_cc_monitor * monitor ) ;
static int cc_generic_monitor_unsuspend ( struct ast_cc_monitor * monitor ) ;
static int cc_generic_monitor_cancel_available_timer ( struct ast_cc_monitor * monitor , int * sched_id ) ;
static void cc_generic_monitor_destructor ( void * private_data ) ;
static struct ast_cc_monitor_callbacks generic_monitor_cbs = {
. type = " generic " ,
. request_cc = cc_generic_monitor_request_cc ,
. suspend = cc_generic_monitor_suspend ,
. unsuspend = cc_generic_monitor_unsuspend ,
. cancel_available_timer = cc_generic_monitor_cancel_available_timer ,
. destructor = cc_generic_monitor_destructor ,
} ;
struct ao2_container * generic_monitors ;
struct generic_monitor_instance {
int core_id ;
int is_suspended ;
int monitoring ;
AST_LIST_ENTRY ( generic_monitor_instance ) next ;
} ;
struct generic_monitor_instance_list {
const char * device_name ;
enum ast_device_state current_state ;
2010-04-12 22:27:07 +00:00
/* If there are multiple instances monitoring the
* same device and one should fail, we need to know
* whether to signal that the device can be recalled.
* The problem is that the device state is not enough
* to check. If a caller has requested CCNR, then the
* fact that the device is available does not indicate
* that the device is ready to be recalled. Instead, as
* soon as one instance of the monitor becomes available
* for a recall, we mark the entire list as being fit
* for recall. If a CCNR request comes in, then we will
* have to mark the list as unfit for recall since this
* is a clear indicator that the person at the monitored
* device has gone away and is actuall not fit to be
* recalled
*/
int fit_for_recall ;
2010-04-09 15:31:32 +00:00
struct ast_event_sub * sub ;
AST_LIST_HEAD_NOLOCK ( , generic_monitor_instance ) list ;
} ;
/*!
* \brief private data for generic device monitor
*/
struct generic_monitor_pvt {
/*!
* We need the device name during destruction so we
* can find the appropriate item to destroy.
*/
const char * device_name ;
/*!
* We need the core ID for similar reasons. Once we
* find the appropriate item in our ao2_container, we
* need to remove the appropriate cc_monitor from the
* list of monitors.
*/
int core_id ;
} ;
static int generic_monitor_hash_fn ( const void * obj , const int flags )
{
const struct generic_monitor_instance_list * generic_list = obj ;
return ast_str_hash ( generic_list - > device_name ) ;
}
static int generic_monitor_cmp_fn ( void * obj , void * arg , int flags )
{
const struct generic_monitor_instance_list * generic_list1 = obj ;
const struct generic_monitor_instance_list * generic_list2 = arg ;
return ! strcmp ( generic_list1 - > device_name , generic_list2 - > device_name ) ? CMP_MATCH | CMP_STOP : 0 ;
}
static struct generic_monitor_instance_list * find_generic_monitor_instance_list ( const char * const device_name )
{
struct generic_monitor_instance_list finder = { . device_name = device_name } ;
return ao2_t_find ( generic_monitors , & finder , OBJ_POINTER , " Finding generic monitor instance list " ) ;
}
static void generic_monitor_instance_list_destructor ( void * obj )
{
struct generic_monitor_instance_list * generic_list = obj ;
struct generic_monitor_instance * generic_instance ;
generic_list - > sub = ast_event_unsubscribe ( generic_list - > sub ) ;
while ( ( generic_instance = AST_LIST_REMOVE_HEAD ( & generic_list - > list , next ) ) ) {
ast_free ( generic_instance ) ;
}
ast_free ( ( char * ) generic_list - > device_name ) ;
}
static void generic_monitor_devstate_cb ( const struct ast_event * event , void * userdata ) ;
static struct generic_monitor_instance_list * create_new_generic_list ( struct ast_cc_monitor * monitor )
{
struct generic_monitor_instance_list * generic_list = ao2_t_alloc ( sizeof ( * generic_list ) ,
generic_monitor_instance_list_destructor , " allocate generic monitor instance list " ) ;
if ( ! generic_list ) {
return NULL ;
}
if ( ! ( generic_list - > device_name = ast_strdup ( monitor - > interface - > device_name ) ) ) {
cc_unref ( generic_list , " Failed to strdup the monitor's device name " ) ;
return NULL ;
}
if ( ! ( generic_list - > sub = ast_event_subscribe ( AST_EVENT_DEVICE_STATE , generic_monitor_devstate_cb ,
" Requesting CC " , NULL , AST_EVENT_IE_DEVICE , AST_EVENT_IE_PLTYPE_STR ,
monitor - > interface - > device_name , AST_EVENT_IE_END ) ) ) {
cc_unref ( generic_list , " Failed to subscribe to device state " ) ;
return NULL ;
}
generic_list - > current_state = ast_device_state ( monitor - > interface - > device_name ) ;
ao2_t_link ( generic_monitors , generic_list , " linking new generic monitor instance list " ) ;
return generic_list ;
}
struct generic_tp_cb_data {
const char * device_name ;
enum ast_device_state new_state ;
} ;
static int generic_monitor_devstate_tp_cb ( void * data )
{
struct generic_tp_cb_data * gtcd = data ;
enum ast_device_state new_state = gtcd - > new_state ;
enum ast_device_state previous_state = gtcd - > new_state ;
const char * monitor_name = gtcd - > device_name ;
struct generic_monitor_instance_list * generic_list ;
struct generic_monitor_instance * generic_instance ;
if ( ! ( generic_list = find_generic_monitor_instance_list ( monitor_name ) ) ) {
/* The most likely cause for this is that we destroyed the monitor in the
* time between subscribing to its device state and the time this executes.
* Not really a big deal.
*/
ast_free ( ( char * ) gtcd - > device_name ) ;
ast_free ( gtcd ) ;
return 0 ;
}
if ( generic_list - > current_state = = new_state ) {
/* The device state hasn't actually changed, so we don't really care */
cc_unref ( generic_list , " Kill reference of generic list in devstate taskprocessor callback " ) ;
ast_free ( ( char * ) gtcd - > device_name ) ;
ast_free ( gtcd ) ;
return 0 ;
}
previous_state = generic_list - > current_state ;
generic_list - > current_state = new_state ;
if ( ( new_state = = AST_DEVICE_NOT_INUSE | | new_state = = AST_DEVICE_UNKNOWN ) & &
( previous_state = = AST_DEVICE_INUSE | | previous_state = = AST_DEVICE_UNAVAILABLE | |
previous_state = = AST_DEVICE_BUSY ) ) {
AST_LIST_TRAVERSE ( & generic_list - > list , generic_instance , next ) {
if ( ! generic_instance - > is_suspended & & generic_instance - > monitoring ) {
generic_instance - > monitoring = 0 ;
2010-04-12 22:27:07 +00:00
generic_list - > fit_for_recall = 1 ;
2010-04-09 15:31:32 +00:00
ast_cc_monitor_callee_available ( generic_instance - > core_id , " Generic monitored party has become available " ) ;
break ;
}
}
}
cc_unref ( generic_list , " Kill reference of generic list in devstate taskprocessor callback " ) ;
ast_free ( ( char * ) gtcd - > device_name ) ;
ast_free ( gtcd ) ;
return 0 ;
}
static void generic_monitor_devstate_cb ( const struct ast_event * event , void * userdata )
{
/* Wow, it's cool that we've picked up on a state change, but we really want
* the actual work to be done in the core's taskprocessor execution thread
* so that all monitor operations can be serialized. Locks?! We don't need
* no steenkin' locks!
*/
struct generic_tp_cb_data * gtcd = ast_calloc ( 1 , sizeof ( * gtcd ) ) ;
if ( ! gtcd ) {
return ;
}
if ( ! ( gtcd - > device_name = ast_strdup ( ast_event_get_ie_str ( event , AST_EVENT_IE_DEVICE ) ) ) ) {
ast_free ( gtcd ) ;
return ;
}
gtcd - > new_state = ast_event_get_ie_uint ( event , AST_EVENT_IE_STATE ) ;
if ( ast_taskprocessor_push ( cc_core_taskprocessor , generic_monitor_devstate_tp_cb , gtcd ) ) {
ast_free ( ( char * ) gtcd - > device_name ) ;
ast_free ( gtcd ) ;
}
}
int ast_cc_available_timer_expire ( const void * data )
{
struct ast_cc_monitor * monitor = ( struct ast_cc_monitor * ) data ;
int res ;
monitor - > available_timer_id = - 1 ;
res = ast_cc_monitor_failed ( monitor - > core_id , monitor - > interface - > device_name , " Available timer expired for monitor " ) ;
cc_unref ( monitor , " Unref reference from scheduler \n " ) ;
return res ;
}
static int cc_generic_monitor_request_cc ( struct ast_cc_monitor * monitor , int * available_timer_id )
{
struct generic_monitor_instance_list * generic_list ;
struct generic_monitor_instance * generic_instance ;
struct generic_monitor_pvt * gen_mon_pvt ;
enum ast_cc_service_type service = monitor - > service_offered ;
int when ;
/* First things first. Native channel drivers will have their private data allocated
* at the time that they tell the core that they can offer CC. Generic is quite a bit
* different, and we wait until this point to allocate our private data.
*/
if ( ! ( gen_mon_pvt = ast_calloc ( 1 , sizeof ( * gen_mon_pvt ) ) ) ) {
return - 1 ;
}
if ( ! ( gen_mon_pvt - > device_name = ast_strdup ( monitor - > interface - > device_name ) ) ) {
ast_free ( gen_mon_pvt ) ;
return - 1 ;
}
gen_mon_pvt - > core_id = monitor - > core_id ;
monitor - > private_data = gen_mon_pvt ;
if ( ! ( generic_list = find_generic_monitor_instance_list ( monitor - > interface - > device_name ) ) ) {
if ( ! ( generic_list = create_new_generic_list ( monitor ) ) ) {
return - 1 ;
}
}
if ( ! ( generic_instance = ast_calloc ( 1 , sizeof ( * generic_instance ) ) ) ) {
/* The generic monitor destructor will take care of the appropriate
* deallocations
*/
cc_unref ( generic_list , " Generic monitor instance failed to allocate " ) ;
return - 1 ;
}
generic_instance - > core_id = monitor - > core_id ;
generic_instance - > monitoring = 1 ;
AST_LIST_INSERT_TAIL ( & generic_list - > list , generic_instance , next ) ;
when = service = = AST_CC_CCBS ? ast_get_ccbs_available_timer ( monitor - > interface - > config_params ) :
ast_get_ccnr_available_timer ( monitor - > interface - > config_params ) ;
* available_timer_id = ast_sched_thread_add ( cc_sched_thread , when * 1000 ,
ast_cc_available_timer_expire , cc_ref ( monitor , " Give the scheduler a monitor reference " ) ) ;
if ( * available_timer_id = = - 1 ) {
cc_unref ( monitor , " Failed to schedule available timer. (monitor) " ) ;
cc_unref ( generic_list , " Failed to schedule available timer. (generic_list) " ) ;
return - 1 ;
}
2010-04-12 22:27:07 +00:00
/* If the new instance was created as CCNR, then that means this device is not currently
* fit for recall even if it previously was.
*/
if ( service = = AST_CC_CCNR | | service = = AST_CC_CCNL ) {
generic_list - > fit_for_recall = 0 ;
}
2010-04-09 15:31:32 +00:00
ast_cc_monitor_request_acked ( monitor - > core_id , " Generic monitor for %s subscribed to device state. " ,
monitor - > interface - > device_name ) ;
cc_unref ( generic_list , " Finished with monitor instance reference in request cc callback " ) ;
return 0 ;
}
static int cc_generic_monitor_suspend ( struct ast_cc_monitor * monitor )
{
struct generic_monitor_instance_list * generic_list ;
struct generic_monitor_instance * generic_instance ;
enum ast_device_state state = ast_device_state ( monitor - > interface - > device_name ) ;
if ( ! ( generic_list = find_generic_monitor_instance_list ( monitor - > interface - > device_name ) ) ) {
return - 1 ;
}
/* First we need to mark this particular monitor as being suspended. */
AST_LIST_TRAVERSE ( & generic_list - > list , generic_instance , next ) {
if ( generic_instance - > core_id = = monitor - > core_id ) {
generic_instance - > is_suspended = 1 ;
break ;
}
}
/* If the device being suspended is currently in use, then we don't need to
* take any further actions
*/
if ( state ! = AST_DEVICE_NOT_INUSE & & state ! = AST_DEVICE_UNKNOWN ) {
cc_unref ( generic_list , " Device is in use. Nothing to do. Unref generic list. " ) ;
return 0 ;
}
/* If the device is not in use, though, then it may be possible to report the
* device's availability using a different monitor which is monitoring the
* same device
*/
AST_LIST_TRAVERSE ( & generic_list - > list , generic_instance , next ) {
if ( ! generic_instance - > is_suspended ) {
ast_cc_monitor_callee_available ( generic_instance - > core_id , " Generic monitored party has become available " ) ;
break ;
}
}
cc_unref ( generic_list , " Done with generic list in suspend callback " ) ;
return 0 ;
}
static int cc_generic_monitor_unsuspend ( struct ast_cc_monitor * monitor )
{
struct generic_monitor_instance * generic_instance ;
struct generic_monitor_instance_list * generic_list = find_generic_monitor_instance_list ( monitor - > interface - > device_name ) ;
enum ast_device_state state = ast_device_state ( monitor - > interface - > device_name ) ;
if ( ! generic_list ) {
return - 1 ;
}
/* If the device is currently available, we can immediately announce
* its availability
*/
if ( state = = AST_DEVICE_NOT_INUSE | | state = = AST_DEVICE_UNKNOWN ) {
ast_cc_monitor_callee_available ( monitor - > core_id , " Generic monitored party has become available " ) ;
}
/* In addition, we need to mark this generic_monitor_instance as not being suspended anymore */
AST_LIST_TRAVERSE ( & generic_list - > list , generic_instance , next ) {
if ( generic_instance - > core_id = = monitor - > core_id ) {
generic_instance - > is_suspended = 0 ;
generic_instance - > monitoring = 1 ;
break ;
}
}
cc_unref ( generic_list , " Done with generic list in cc_generic_monitor_unsuspend " ) ;
return 0 ;
}
static int cc_generic_monitor_cancel_available_timer ( struct ast_cc_monitor * monitor , int * sched_id )
{
ast_assert ( sched_id ! = NULL ) ;
if ( * sched_id = = - 1 ) {
return 0 ;
}
ast_log_dynamic_level ( cc_logger_level , " Core %d: Canceling generic monitor available timer for monitor %s \n " ,
monitor - > core_id , monitor - > interface - > device_name ) ;
if ( ! ast_sched_thread_del ( cc_sched_thread , * sched_id ) ) {
cc_unref ( monitor , " Remove scheduler's reference to the monitor " ) ;
}
* sched_id = - 1 ;
return 0 ;
}
static void cc_generic_monitor_destructor ( void * private_data )
{
struct generic_monitor_pvt * gen_mon_pvt = private_data ;
struct generic_monitor_instance_list * generic_list ;
struct generic_monitor_instance * generic_instance ;
if ( ! private_data ) {
/* If the private data is NULL, that means that the monitor hasn't even
* been created yet, but that the destructor was called. While this sort
* of behavior is useful for native monitors, with a generic one, there is
* nothing in particular to do.
*/
return ;
}
ast_log_dynamic_level ( cc_logger_level , " Core %d: Destroying generic monitor %s \n " ,
gen_mon_pvt - > core_id , gen_mon_pvt - > device_name ) ;
if ( ! ( generic_list = find_generic_monitor_instance_list ( gen_mon_pvt - > device_name ) ) ) {
/* If there's no generic list, that means that the monitor is being destroyed
* before we actually got to request CC. Not a biggie. Same in the situation
* below if the list traversal should complete without finding an entry.
*/
ast_free ( ( char * ) gen_mon_pvt - > device_name ) ;
ast_free ( gen_mon_pvt ) ;
return ;
}
AST_LIST_TRAVERSE_SAFE_BEGIN ( & generic_list - > list , generic_instance , next ) {
if ( generic_instance - > core_id = = gen_mon_pvt - > core_id ) {
AST_LIST_REMOVE_CURRENT ( next ) ;
ast_free ( generic_instance ) ;
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( AST_LIST_EMPTY ( & generic_list - > list ) ) {
/* No more monitors with this device name exist. Time to unlink this
* list from the container
*/
ao2_t_unlink ( generic_monitors , generic_list , " Generic list is empty. Unlink it from the container " ) ;
2010-04-12 22:27:07 +00:00
} else {
/* There are still instances for this particular device. The situation
* may be that we were attempting a CC recall and a failure occurred, perhaps
* on the agent side. If a failure happens here and the device being monitored
* is available, then we need to signal on the first unsuspended instance that
* the device is available for recall.
*/
/* First things first. We don't even want to consider this action if
* the device in question isn't available right now.
*/
if ( generic_list - > fit_for_recall & & ( generic_list - > current_state = = AST_DEVICE_NOT_INUSE | |
generic_list - > current_state = = AST_DEVICE_UNKNOWN ) ) {
AST_LIST_TRAVERSE ( & generic_list - > list , generic_instance , next ) {
if ( ! generic_instance - > is_suspended & & generic_instance - > monitoring ) {
ast_cc_monitor_callee_available ( generic_instance - > core_id , " Signaling generic monitor "
" availability due to other instance's failure. " ) ;
break ;
}
}
}
2010-04-09 15:31:32 +00:00
}
cc_unref ( generic_list , " Done with generic list in generic monitor destructor " ) ;
ast_free ( ( char * ) gen_mon_pvt - > device_name ) ;
ast_free ( gen_mon_pvt ) ;
}
static void cc_interface_destroy ( void * data )
{
struct ast_cc_interface * interface = data ;
ast_log_dynamic_level ( cc_logger_level , " Destroying cc interface %s \n " , interface - > device_name ) ;
ast_cc_config_params_destroy ( interface - > config_params ) ;
}
/*!
* \brief Data regarding an extension monitor's child's dialstrings
*
* \details
* In developing CCSS, we had most aspects of its operation finished,
* but there was one looming problem that we had failed to get right.
* In our design document, we stated that when a CC recall occurs, all
* endpoints that had been dialed originally would be called back.
* Unfortunately, our implementation only allowed for devices which had
* active monitors to inhabit the CC_INTERFACES channel variable, thus
* making the automated recall only call monitored devices.
*
* Devices that were not CC-capable, or devices which failed CC at some
* point during the process would not make it into the CC_INTERFACES
* channel variable. This struct is meant as a remedy for the problem.
*/
struct extension_child_dialstring {
/*!
* \brief the original dialstring used to call a particular device
*
* \details
* When someone dials a particular endpoint, the dialstring used in
* the dialplan is copied into this buffer. What's important here is
* that this is the ORIGINAL dialstring, not the dialstring saved on
* a device monitor. The dialstring on a device monitor is what should
* be used when recalling that device. The two dialstrings may not be
* the same.
*
* By keeping a copy of the original dialstring used, we can fall back
* to using it if the device either does not ever offer CC or if the
* device at some point fails for some reason, such as a timer expiration.
*/
char original_dialstring [ AST_CHANNEL_NAME ] ;
/*!
* \brief The name of the device being dialed
*
* \details
* This serves mainly as a key when searching for a particular dialstring.
* For instance, let's say that we have called device SIP/400@somepeer. This
* device offers call completion, but then due to some unforeseen circumstance,
* this device backs out and makes CC unavailable. When that happens, we need
* to find the dialstring that corresponds to that device, and we use the
* stored device name as a way to find it.
*
2010-06-08 14:38:18 +00:00
* \note There is one particular case where the device name stored here
2010-04-09 15:31:32 +00:00
* will be empty. This is the case where we fail to request a channel, but we
* still can make use of generic call completion. In such a case, since we never
* were able to request the channel, we can't find what its device name is. In
* this case, however, it is not important because the dialstring is guaranteed
* to be the same both here and in the device monitor.
*/
char device_name [ AST_CHANNEL_NAME ] ;
/*!
* \brief Is this structure valid for use in CC_INTERFACES?
*
* \details
* When this structure is first created, all information stored here is planned
* to be used, so we set the is_valid flag. However, if a device offers call
* completion, it will potentially have its own dialstring to use for the recall,
* so we find this structure and clear the is_valid flag. By clearing the is_valid
* flag, we won't try to populate the CC_INTERFACES variable with the dialstring
* stored in this struct. Now, if later, the device which had offered CC should fail,
* perhaps due to a timer expiration, then we need to re-set the is_valid flag. This
* way, we still will end up placing a call to the device again, and the dialstring
* used will be the same as was originally used.
*/
int is_valid ;
AST_LIST_ENTRY ( extension_child_dialstring ) next ;
} ;
/*!
* \brief Private data for an extension monitor
*/
struct extension_monitor_pvt {
AST_LIST_HEAD_NOLOCK ( , extension_child_dialstring ) child_dialstrings ;
} ;
static void cc_extension_monitor_destructor ( void * private_data )
{
struct extension_monitor_pvt * extension_pvt = private_data ;
struct extension_child_dialstring * child_dialstring ;
/* This shouldn't be possible, but I'm paranoid */
if ( ! extension_pvt ) {
return ;
}
while ( ( child_dialstring = AST_LIST_REMOVE_HEAD ( & extension_pvt - > child_dialstrings , next ) ) ) {
ast_free ( child_dialstring ) ;
}
ast_free ( extension_pvt ) ;
}
static void cc_monitor_destroy ( void * data )
{
struct ast_cc_monitor * monitor = data ;
/* During the monitor creation process, it is possible for this
* function to be called prior to when callbacks are assigned
* to the monitor. Also, extension monitors do not have callbacks
* assigned to them, so we wouldn't want to segfault when we try
* to destroy one of them.
*/
ast_log_dynamic_level ( cc_logger_level , " Core %d: Calling destructor for monitor %s \n " ,
monitor - > core_id , monitor - > interface - > device_name ) ;
if ( monitor - > interface - > monitor_class = = AST_CC_EXTENSION_MONITOR ) {
cc_extension_monitor_destructor ( monitor - > private_data ) ;
}
if ( monitor - > callbacks ) {
monitor - > callbacks - > destructor ( monitor - > private_data ) ;
}
cc_unref ( monitor - > interface , " Unreffing tree's reference to interface " ) ;
ast_free ( monitor - > dialstring ) ;
}
static void cc_interface_tree_destroy ( void * data )
{
struct cc_monitor_tree * cc_interface_tree = data ;
struct ast_cc_monitor * monitor ;
while ( ( monitor = AST_LIST_REMOVE_HEAD ( cc_interface_tree , next ) ) ) {
if ( monitor - > callbacks ) {
monitor - > callbacks - > cancel_available_timer ( monitor , & monitor - > available_timer_id ) ;
}
cc_unref ( monitor , " Destroying all monitors " ) ;
}
AST_LIST_HEAD_DESTROY ( cc_interface_tree ) ;
}
/*!
* This counter is used for assigning unique ids
* to CC-enabled dialed interfaces.
*/
static int dialed_cc_interface_counter ;
/*!
* \internal
* \brief data stored in CC datastore
*
* The datastore creates a list of interfaces that were
* dialed, including both extensions and devices. In addition
* to the intrinsic data of the tree, some extra information
* is needed for use by app_dial.
*/
struct dialed_cc_interfaces {
/*!
* This value serves a dual-purpose. When dial starts, if the
* dialed_cc_interfaces datastore currently exists on the calling
* channel, then the dial_parent_id will serve as a means of
* letting the new extension cc_monitor we create know
* who his parent is. This value will be the extension
* cc_monitor that dialed the local channel that resulted
* in the new Dial app being called.
*
* In addition, once an extension cc_monitor is created,
* the dial_parent_id will be changed to the id of that newly
* created interface. This way, device interfaces created from
* receiving AST_CONTROL_CC frames can use this field to determine
* who their parent extension interface should be.
*/
unsigned int dial_parent_id ;
/*!
* Identifier for the potential CC request that may be made
* based on this call. Even though an instance of the core may
* not be made (since the caller may not request CC), we allocate
* a new core_id at the beginning of the call so that recipient
* channel drivers can have the information handy just in case
* the caller does end up requesting CC.
*/
int core_id ;
/*!
* When a new Dial application is started, and the datastore
* already exists on the channel, we can determine if we
* should be adding any new interface information to tree.
*/
char ignore ;
/*!
* When it comes time to offer CC to the caller, we only want to offer
* it to the original incoming channel. For nested Dials and outbound
* channels, it is incorrect to attempt such a thing. This flag indicates
* if the channel to which this datastore is attached may be legally
* offered CC when the call is finished.
*/
char is_original_caller ;
/*!
* Reference-counted "tree" of interfaces.
*/
struct cc_monitor_tree * interface_tree ;
} ;
/*!
* \internal
* \brief Destructor function for cc_interfaces datastore
*
* This function will free the actual datastore and drop
* the refcount for the monitor tree by one. In cases
* where CC can actually be used, this unref will not
* result in the destruction of the monitor tree, because
* the CC core will still have a reference.
*
* \param data The dialed_cc_interfaces struct to destroy
*/
static void dialed_cc_interfaces_destroy ( void * data )
{
struct dialed_cc_interfaces * cc_interfaces = data ;
cc_unref ( cc_interfaces - > interface_tree , " Unref dial's ref to monitor tree " ) ;
ast_free ( cc_interfaces ) ;
}
/*!
* \internal
* \brief Duplicate callback for cc_interfaces datastore
*
* Integers are copied by value, but the monitor tree
* is done via a shallow copy and a bump of the refcount.
* This way, sub-Dials will be appending interfaces onto
* the same list as this call to Dial.
*
* \param data The old dialed_cc_interfaces we want to copy
* \retval NULL Could not allocate memory for new dialed_cc_interfaces
* \retval non-NULL The new copy of the dialed_cc_interfaces
*/
static void * dialed_cc_interfaces_duplicate ( void * data )
{
struct dialed_cc_interfaces * old_cc_interfaces = data ;
struct dialed_cc_interfaces * new_cc_interfaces = ast_calloc ( 1 , sizeof ( * new_cc_interfaces ) ) ;
if ( ! new_cc_interfaces ) {
return NULL ;
}
new_cc_interfaces - > ignore = old_cc_interfaces - > ignore ;
new_cc_interfaces - > dial_parent_id = old_cc_interfaces - > dial_parent_id ;
new_cc_interfaces - > is_original_caller = 0 ;
cc_ref ( old_cc_interfaces - > interface_tree , " New ref due to duplication of monitor tree " ) ;
new_cc_interfaces - > core_id = old_cc_interfaces - > core_id ;
new_cc_interfaces - > interface_tree = old_cc_interfaces - > interface_tree ;
return new_cc_interfaces ;
}
/*!
* \internal
* \brief information regarding the dialed_cc_interfaces datastore
*
* The dialed_cc_interfaces datastore is responsible for keeping track
* of what CC-enabled interfaces have been dialed by the caller. For
* more information regarding the actual structure of the tree, see
* the documentation provided in include/asterisk/ccss.h
*/
static const struct ast_datastore_info dialed_cc_interfaces_info = {
. type = " Dial CC Interfaces " ,
. duplicate = dialed_cc_interfaces_duplicate ,
. destroy = dialed_cc_interfaces_destroy ,
} ;
static struct extension_monitor_pvt * extension_monitor_pvt_init ( void )
{
struct extension_monitor_pvt * ext_pvt = ast_calloc ( 1 , sizeof ( * ext_pvt ) ) ;
if ( ! ext_pvt ) {
return NULL ;
}
AST_LIST_HEAD_INIT_NOLOCK ( & ext_pvt - > child_dialstrings ) ;
return ext_pvt ;
}
void ast_cc_extension_monitor_add_dialstring ( struct ast_channel * incoming , const char * const dialstring , const char * const device_name )
{
struct ast_datastore * cc_datastore ;
struct dialed_cc_interfaces * cc_interfaces ;
struct ast_cc_monitor * monitor ;
struct extension_monitor_pvt * extension_pvt ;
struct extension_child_dialstring * child_dialstring ;
struct cc_monitor_tree * interface_tree ;
int id ;
ast_channel_lock ( incoming ) ;
if ( ! ( cc_datastore = ast_channel_datastore_find ( incoming , & dialed_cc_interfaces_info , NULL ) ) ) {
ast_channel_unlock ( incoming ) ;
return ;
}
cc_interfaces = cc_datastore - > data ;
interface_tree = cc_interfaces - > interface_tree ;
id = cc_interfaces - > dial_parent_id ;
ast_channel_unlock ( incoming ) ;
AST_LIST_LOCK ( interface_tree ) ;
AST_LIST_TRAVERSE ( interface_tree , monitor , next ) {
if ( monitor - > id = = id ) {
break ;
}
}
if ( ! monitor ) {
AST_LIST_UNLOCK ( interface_tree ) ;
return ;
}
extension_pvt = monitor - > private_data ;
if ( ! ( child_dialstring = ast_calloc ( 1 , sizeof ( * child_dialstring ) ) ) ) {
AST_LIST_UNLOCK ( interface_tree ) ;
return ;
}
ast_copy_string ( child_dialstring - > original_dialstring , dialstring , sizeof ( child_dialstring - > original_dialstring ) ) ;
ast_copy_string ( child_dialstring - > device_name , device_name , sizeof ( child_dialstring - > device_name ) ) ;
child_dialstring - > is_valid = 1 ;
AST_LIST_INSERT_TAIL ( & extension_pvt - > child_dialstrings , child_dialstring , next ) ;
AST_LIST_UNLOCK ( interface_tree ) ;
}
static void cc_extension_monitor_change_is_valid ( struct cc_core_instance * core_instance , unsigned int parent_id , const char * const device_name , int is_valid )
{
struct ast_cc_monitor * monitor_iter ;
struct extension_monitor_pvt * extension_pvt ;
struct extension_child_dialstring * child_dialstring ;
AST_LIST_TRAVERSE ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > id = = parent_id ) {
break ;
}
}
if ( ! monitor_iter ) {
return ;
}
extension_pvt = monitor_iter - > private_data ;
AST_LIST_TRAVERSE ( & extension_pvt - > child_dialstrings , child_dialstring , next ) {
if ( ! strcmp ( child_dialstring - > device_name , device_name ) ) {
child_dialstring - > is_valid = is_valid ;
break ;
}
}
}
/*!
* \internal
* \brief Allocate and initialize an "extension" interface for CC purposes
*
* When app_dial starts, this function is called in order to set up the
* information about the extension in which this Dial is occurring. Any
* devices dialed will have this particular cc_monitor as a parent.
*
* \param exten Extension from which Dial is occurring
* \param context Context to which exten belongs
* \param parent_id What should we set the parent_id of this interface to?
* \retval NULL Memory allocation failure
* \retval non-NULL The newly-created cc_monitor for the extension
*/
static struct ast_cc_monitor * cc_extension_monitor_init ( const char * const exten , const char * const context , const unsigned int parent_id )
{
struct ast_str * str = ast_str_alloca ( 2 * AST_MAX_EXTENSION ) ;
struct ast_cc_interface * cc_interface ;
struct ast_cc_monitor * monitor ;
ast_str_set ( & str , 0 , " %s@%s " , exten , context ) ;
if ( ! ( cc_interface = ao2_t_alloc ( sizeof ( * cc_interface ) + ast_str_strlen ( str ) , cc_interface_destroy ,
" Allocating new ast_cc_interface " ) ) ) {
return NULL ;
}
if ( ! ( monitor = ao2_t_alloc ( sizeof ( * monitor ) , cc_monitor_destroy , " Allocating new ast_cc_monitor " ) ) ) {
cc_unref ( cc_interface , " failed to allocate the monitor, so unref the interface " ) ;
return NULL ;
}
if ( ! ( monitor - > private_data = extension_monitor_pvt_init ( ) ) ) {
cc_unref ( monitor , " Failed to initialize extension monitor private data. uref monitor " ) ;
cc_unref ( cc_interface , " Failed to initialize extension monitor private data. unref cc_interface " ) ;
}
monitor - > id = ast_atomic_fetchadd_int ( & dialed_cc_interface_counter , + 1 ) ;
monitor - > parent_id = parent_id ;
cc_interface - > monitor_type = " extension " ;
cc_interface - > monitor_class = AST_CC_EXTENSION_MONITOR ;
strcpy ( cc_interface - > device_name , ast_str_buffer ( str ) ) ;
monitor - > interface = cc_interface ;
ast_log_dynamic_level ( cc_logger_level , " Created an extension cc interface for '%s' with id %d and parent %d \n " , cc_interface - > device_name , monitor - > id , monitor - > parent_id ) ;
return monitor ;
}
/*!
* \internal
* \brief allocate dialed_cc_interfaces datastore and initialize fields
*
* This function is called when Situation 1 occurs in ast_cc_call_init.
* See that function for more information on what Situation 1 is.
*
* In this particular case, we have to do a lot of memory allocation in order
* to create the datastore, the data for the datastore, the tree of interfaces
* that we'll be adding to, and the initial extension interface for this Dial
* attempt.
*
* \param chan The channel onto which the datastore should be added.
* \retval -1 An error occurred
* \retval 0 Success
*/
static int cc_interfaces_datastore_init ( struct ast_channel * chan ) {
struct dialed_cc_interfaces * interfaces ;
struct ast_cc_monitor * monitor ;
struct ast_datastore * dial_cc_datastore ;
/*XXX This may be a bit controversial. In an attempt to not allocate
* extra resources, I make sure that a future request will be within
* limits. The problem here is that it is reasonable to think that
* even if we're not within the limits at this point, we may be by
* the time the requestor will have made his request. This may be
* deleted at some point.
*/
if ( ! ast_cc_request_is_within_limits ( ) ) {
return 0 ;
}
if ( ! ( interfaces = ast_calloc ( 1 , sizeof ( * interfaces ) ) ) ) {
return - 1 ;
}
if ( ! ( monitor = cc_extension_monitor_init ( S_OR ( chan - > macroexten , chan - > exten ) , S_OR ( chan - > macrocontext , chan - > context ) , 0 ) ) ) {
ast_free ( interfaces ) ;
return - 1 ;
}
if ( ! ( dial_cc_datastore = ast_datastore_alloc ( & dialed_cc_interfaces_info , NULL ) ) ) {
cc_unref ( monitor , " Could not allocate the dialed interfaces datastore. Unreffing monitor " ) ;
ast_free ( interfaces ) ;
return - 1 ;
}
if ( ! ( interfaces - > interface_tree = ao2_t_alloc ( sizeof ( * interfaces - > interface_tree ) , cc_interface_tree_destroy ,
" Allocate monitor tree " ) ) ) {
ast_datastore_free ( dial_cc_datastore ) ;
cc_unref ( monitor , " Could not allocate monitor tree on dialed interfaces datastore. Unreffing monitor " ) ;
ast_free ( interfaces ) ;
return - 1 ;
}
/* Finally, all that allocation is done... */
AST_LIST_HEAD_INIT ( interfaces - > interface_tree ) ;
AST_LIST_INSERT_TAIL ( interfaces - > interface_tree , monitor , next ) ;
cc_ref ( monitor , " List's reference to extension monitor " ) ;
dial_cc_datastore - > data = interfaces ;
dial_cc_datastore - > inheritance = DATASTORE_INHERIT_FOREVER ;
interfaces - > dial_parent_id = monitor - > id ;
interfaces - > core_id = monitor - > core_id = ast_atomic_fetchadd_int ( & core_id_counter , + 1 ) ;
interfaces - > is_original_caller = 1 ;
ast_channel_lock ( chan ) ;
ast_channel_datastore_add ( chan , dial_cc_datastore ) ;
ast_channel_unlock ( chan ) ;
cc_unref ( monitor , " Unreffing allocation's reference " ) ;
return 0 ;
}
/*!
* \internal
* \brief Call a monitor's destructor before the monitor has been allocated
* \since 1.8
*
* \param monitor_type The type of monitor callbacks to use when calling the destructor
* \param private_data Data allocated by a channel driver that must be freed
*
* \details
* I'll admit, this is a bit evil.
*
* When a channel driver determines that it can offer a call completion service to
* a caller, it is very likely that the channel driver will need to allocate some
* data so that when the time comes to request CC, the channel driver will have the
* necessary data at hand.
*
* The problem is that there are many places where failures may occur before the monitor
* has been properly allocated and had its callbacks assigned to it. If one of these
* failures should occur, then we still need to let the channel driver know that it
* must destroy the data that it allocated.
*
* \return Nothing
*/
static void call_destructor_with_no_monitor ( const char * const monitor_type , void * private_data )
{
const struct ast_cc_monitor_callbacks * monitor_callbacks = find_monitor_callbacks ( monitor_type ) ;
if ( ! monitor_callbacks ) {
return ;
}
monitor_callbacks - > destructor ( private_data ) ;
}
/*!
* \internal
* \brief Allocate and intitialize a device cc_monitor
*
* For all intents and purposes, this is the same as
* cc_extension_monitor_init, except that there is only
* a single parameter used for naming the interface.
*
* This function is called when handling AST_CONTROL_CC frames.
* The device has reported that CC is possible, so we add it
* to the interface_tree.
*
* Note that it is not necessarily erroneous to add the same
* device to the tree twice. If the same device is called by
* two different extension during the same call, then
* that is a legitimate situation. Of course, I'm pretty sure
* the dialed_interfaces global datastore will not allow that
* to happen anyway.
*
* \param device_name The name of the device being added to the tree
* \param dialstring The dialstring used to dial the device being added
* \param parent_id The parent of this new tree node.
* \retval NULL Memory allocation failure
* \retval non-NULL The new ast_cc_interface created.
*/
static struct ast_cc_monitor * cc_device_monitor_init ( const char * const device_name , const char * const dialstring , const struct cc_control_payload * cc_data , int core_id )
{
struct ast_cc_interface * cc_interface ;
struct ast_cc_monitor * monitor ;
size_t device_name_len = strlen ( device_name ) ;
int parent_id = cc_data - > parent_interface_id ;
if ( ! ( cc_interface = ao2_t_alloc ( sizeof ( * cc_interface ) + device_name_len , cc_interface_destroy ,
" Allocating new ast_cc_interface " ) ) ) {
return NULL ;
}
if ( ! ( cc_interface - > config_params = ast_cc_config_params_init ( ) ) ) {
cc_unref ( cc_interface , " Failed to allocate config params, unref interface " ) ;
return NULL ;
}
if ( ! ( monitor = ao2_t_alloc ( sizeof ( * monitor ) , cc_monitor_destroy , " Allocating new ast_cc_monitor " ) ) ) {
cc_unref ( cc_interface , " Failed to allocate monitor, unref interface " ) ;
return NULL ;
}
if ( ! ( monitor - > dialstring = ast_strdup ( dialstring ) ) ) {
cc_unref ( monitor , " Failed to copy dialable name. Unref monitor " ) ;
cc_unref ( cc_interface , " Failed to copy dialable name " ) ;
return NULL ;
}
if ( ! ( monitor - > callbacks = find_monitor_callbacks ( cc_data - > monitor_type ) ) ) {
cc_unref ( monitor , " Failed to find monitor callbacks. Unref monitor " ) ;
cc_unref ( cc_interface , " Failed to find monitor callbacks " ) ;
return NULL ;
}
strcpy ( cc_interface - > device_name , device_name ) ;
monitor - > id = ast_atomic_fetchadd_int ( & dialed_cc_interface_counter , + 1 ) ;
monitor - > parent_id = parent_id ;
monitor - > core_id = core_id ;
monitor - > service_offered = cc_data - > service ;
monitor - > private_data = cc_data - > private_data ;
cc_interface - > monitor_type = cc_data - > monitor_type ;
cc_interface - > monitor_class = AST_CC_DEVICE_MONITOR ;
monitor - > interface = cc_interface ;
monitor - > available_timer_id = - 1 ;
ast_cc_copy_config_params ( cc_interface - > config_params , & cc_data - > config_params ) ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: Created a device cc interface for '%s' with id %d and parent %d \n " ,
monitor - > core_id , cc_interface - > device_name , monitor - > id , monitor - > parent_id ) ;
return monitor ;
}
/*!
* \details
* Unless we are ignoring CC for some reason, we will always
* call this function when we read an AST_CONTROL_CC frame
* from an outbound channel.
*
* This function will call cc_device_monitor_init to
* create the new cc_monitor for the device from which
* we read the frame. In addition, the new device will be added
* to the monitor tree on the dialed_cc_interfaces datastore
* on the inbound channel.
*
* If this is the first AST_CONTROL_CC frame that we have handled
* for this call, then we will also initialize the CC core for
* this call.
*/
void ast_handle_cc_control_frame ( struct ast_channel * inbound , struct ast_channel * outbound , void * frame_data )
{
char * device_name ;
char * dialstring ;
struct ast_cc_monitor * monitor ;
struct ast_datastore * cc_datastore ;
struct dialed_cc_interfaces * cc_interfaces ;
struct cc_control_payload * cc_data = frame_data ;
struct cc_core_instance * core_instance ;
device_name = cc_data - > device_name ;
dialstring = cc_data - > dialstring ;
ast_channel_lock ( inbound ) ;
if ( ! ( cc_datastore = ast_channel_datastore_find ( inbound , & dialed_cc_interfaces_info , NULL ) ) ) {
ast_log ( LOG_WARNING , " Unable to retrieve CC datastore while processing CC frame from '%s'. CC services will be unavailable. \n " , device_name ) ;
ast_channel_unlock ( inbound ) ;
call_destructor_with_no_monitor ( cc_data - > monitor_type , cc_data - > private_data ) ;
return ;
}
cc_interfaces = cc_datastore - > data ;
if ( cc_interfaces - > ignore ) {
ast_channel_unlock ( inbound ) ;
call_destructor_with_no_monitor ( cc_data - > monitor_type , cc_data - > private_data ) ;
return ;
}
if ( ! cc_interfaces - > is_original_caller ) {
/* If the is_original_caller is not set on the *inbound* channel, then
* it must be a local channel. As such, we do not want to create a core instance
* or an agent for the local channel. Instead, we want to pass this along to the
* other side of the local channel so that the original caller can benefit.
*/
ast_channel_unlock ( inbound ) ;
ast_indicate_data ( inbound , AST_CONTROL_CC , cc_data , sizeof ( * cc_data ) ) ;
return ;
}
core_instance = find_cc_core_instance ( cc_interfaces - > core_id ) ;
if ( ! core_instance ) {
core_instance = cc_core_init_instance ( inbound , cc_interfaces - > interface_tree ,
cc_interfaces - > core_id , cc_data ) ;
if ( ! core_instance ) {
cc_interfaces - > ignore = 1 ;
ast_channel_unlock ( inbound ) ;
call_destructor_with_no_monitor ( cc_data - > monitor_type , cc_data - > private_data ) ;
return ;
}
}
ast_channel_unlock ( inbound ) ;
/* Yeah this kind of sucks, but luckily most people
* aren't dialing thousands of interfaces on every call
*
* This traversal helps us to not create duplicate monitors in
* case a device queues multiple CC control frames.
*/
AST_LIST_LOCK ( cc_interfaces - > interface_tree ) ;
AST_LIST_TRAVERSE ( cc_interfaces - > interface_tree , monitor , next ) {
if ( ! strcmp ( monitor - > interface - > device_name , device_name ) ) {
ast_log_dynamic_level ( cc_logger_level , " Core %d: Device %s sent us multiple CC control frames. Ignoring those beyond the first. \n " ,
core_instance - > core_id , device_name ) ;
AST_LIST_UNLOCK ( cc_interfaces - > interface_tree ) ;
cc_unref ( core_instance , " Returning early from ast_handle_cc_control_frame. Unref core_instance " ) ;
call_destructor_with_no_monitor ( cc_data - > monitor_type , cc_data - > private_data ) ;
return ;
}
}
AST_LIST_UNLOCK ( cc_interfaces - > interface_tree ) ;
if ( ! ( monitor = cc_device_monitor_init ( device_name , dialstring , cc_data , core_instance - > core_id ) ) ) {
ast_log ( LOG_WARNING , " Unable to create CC device interface for '%s'. CC services will be unavailable on this interface. \n " , device_name ) ;
cc_unref ( core_instance , " Returning early from ast_handle_cc_control_frame. Unref core_instance " ) ;
call_destructor_with_no_monitor ( cc_data - > monitor_type , cc_data - > private_data ) ;
return ;
}
AST_LIST_LOCK ( cc_interfaces - > interface_tree ) ;
cc_ref ( monitor , " monitor tree's reference to the monitor " ) ;
AST_LIST_INSERT_TAIL ( cc_interfaces - > interface_tree , monitor , next ) ;
AST_LIST_UNLOCK ( cc_interfaces - > interface_tree ) ;
cc_extension_monitor_change_is_valid ( core_instance , monitor - > parent_id , monitor - > interface - > device_name , 0 ) ;
manager_event ( EVENT_FLAG_CC , " CCAvailable " ,
" CoreID: %d \r \n "
" Callee: %s \r \n "
" Service: %s \r \n " ,
cc_interfaces - > core_id , device_name , cc_service_to_string ( cc_data - > service )
) ;
cc_unref ( core_instance , " Done with core_instance after handling CC control frame " ) ;
cc_unref ( monitor , " Unref reference from allocating monitor " ) ;
}
int ast_cc_call_init ( struct ast_channel * chan , int * ignore_cc )
{
/* There are three situations to deal with here:
*
* 1. The channel does not have a dialed_cc_interfaces datastore on
* it. This means that this is the first time that Dial has
* been called. We need to create/initialize the datastore.
*
* 2. The channel does have a cc_interface datastore on it and
* the "ignore" indicator is 0. This means that a Local channel
* was called by a "parent" dial. We can check the datastore's
* parent field to see who the root of this particular dial tree
* is.
*
* 3. The channel does have a cc_interface datastore on it and
* the "ignore" indicator is 1. This means that a second Dial call
* is being made from an extension. In this case, we do not
* want to make any additions/modifications to the datastore. We
* will instead set a flag to indicate that CCSS is completely
* disabled for this Dial attempt.
*/
struct ast_datastore * cc_interfaces_datastore ;
struct dialed_cc_interfaces * interfaces ;
struct ast_cc_monitor * monitor ;
struct ast_cc_config_params * cc_params ;
ast_channel_lock ( chan ) ;
cc_params = ast_channel_get_cc_config_params ( chan ) ;
if ( ! cc_params ) {
ast_channel_unlock ( chan ) ;
return - 1 ;
}
if ( ast_get_cc_agent_policy ( cc_params ) = = AST_CC_AGENT_NEVER ) {
/* We can't offer CC to this caller anyway, so don't bother with CC on this call
*/
* ignore_cc = 1 ;
ast_channel_unlock ( chan ) ;
ast_log_dynamic_level ( cc_logger_level , " Agent policy for %s is 'never'. CC not possible \n " , chan - > name ) ;
return 0 ;
}
if ( ! ( cc_interfaces_datastore = ast_channel_datastore_find ( chan , & dialed_cc_interfaces_info , NULL ) ) ) {
/* Situation 1 has occurred */
ast_channel_unlock ( chan ) ;
return cc_interfaces_datastore_init ( chan ) ;
}
interfaces = cc_interfaces_datastore - > data ;
ast_channel_unlock ( chan ) ;
if ( interfaces - > ignore ) {
/* Situation 3 has occurred */
* ignore_cc = 1 ;
ast_log_dynamic_level ( cc_logger_level , " Datastore is present with ignore flag set. Ignoring CC offers on this call \n " ) ;
return 0 ;
}
/* Situation 2 has occurred */
if ( ! ( monitor = cc_extension_monitor_init ( S_OR ( chan - > macroexten , chan - > exten ) ,
S_OR ( chan - > macrocontext , chan - > context ) , interfaces - > dial_parent_id ) ) ) {
return - 1 ;
}
monitor - > core_id = interfaces - > core_id ;
AST_LIST_LOCK ( interfaces - > interface_tree ) ;
cc_ref ( monitor , " monitor tree's reference to the monitor " ) ;
AST_LIST_INSERT_TAIL ( interfaces - > interface_tree , monitor , next ) ;
AST_LIST_UNLOCK ( interfaces - > interface_tree ) ;
interfaces - > dial_parent_id = monitor - > id ;
cc_unref ( monitor , " Unref monitor's allocation reference " ) ;
return 0 ;
}
int ast_cc_request_is_within_limits ( void )
{
return cc_request_count < global_cc_max_requests ;
}
int ast_cc_get_current_core_id ( struct ast_channel * chan )
{
struct ast_datastore * datastore ;
struct dialed_cc_interfaces * cc_interfaces ;
int core_id_return ;
ast_channel_lock ( chan ) ;
if ( ! ( datastore = ast_channel_datastore_find ( chan , & dialed_cc_interfaces_info , NULL ) ) ) {
ast_channel_unlock ( chan ) ;
return - 1 ;
}
cc_interfaces = datastore - > data ;
core_id_return = cc_interfaces - > ignore ? - 1 : cc_interfaces - > core_id ;
ast_channel_unlock ( chan ) ;
return core_id_return ;
}
static long count_agents ( const char * const caller , const int core_id_exception )
{
struct count_agents_cb_data data = { . core_id_exception = core_id_exception , } ;
ao2_t_callback_data ( cc_core_instances , OBJ_NODATA , count_agents_cb , ( char * ) caller , & data , " Counting agents " ) ;
ast_log_dynamic_level ( cc_logger_level , " Counted %d agents \n " , data . count ) ;
return data . count ;
}
static void kill_duplicate_offers ( char * caller )
{
unsigned long match_flags = MATCH_NO_REQUEST ;
ao2_t_callback_data ( cc_core_instances , OBJ_UNLINK | OBJ_NODATA , match_agent , caller , & match_flags , " Killing duplicate offers " ) ;
}
static void check_callback_sanity ( const struct ast_cc_agent_callbacks * callbacks )
{
ast_assert ( callbacks - > init ! = NULL ) ;
ast_assert ( callbacks - > start_offer_timer ! = NULL ) ;
ast_assert ( callbacks - > stop_offer_timer ! = NULL ) ;
ast_assert ( callbacks - > ack ! = NULL ) ;
ast_assert ( callbacks - > status_request ! = NULL ) ;
ast_assert ( callbacks - > start_monitoring ! = NULL ) ;
ast_assert ( callbacks - > callee_available ! = NULL ) ;
ast_assert ( callbacks - > destructor ! = NULL ) ;
}
static void agent_destroy ( void * data )
{
struct ast_cc_agent * agent = data ;
if ( agent - > callbacks ) {
agent - > callbacks - > destructor ( agent ) ;
}
ast_cc_config_params_destroy ( agent - > cc_params ) ;
}
static struct ast_cc_agent * cc_agent_init ( struct ast_channel * caller_chan ,
const char * const caller_name , const int core_id ,
struct cc_monitor_tree * interface_tree )
{
struct ast_cc_agent * agent ;
struct ast_cc_config_params * cc_params ;
if ( ! ( agent = ao2_t_alloc ( sizeof ( * agent ) + strlen ( caller_name ) , agent_destroy ,
" Allocating new ast_cc_agent " ) ) ) {
return NULL ;
}
agent - > core_id = core_id ;
strcpy ( agent - > device_name , caller_name ) ;
cc_params = ast_channel_get_cc_config_params ( caller_chan ) ;
if ( ! cc_params ) {
cc_unref ( agent , " Could not get channel config params. " ) ;
return NULL ;
}
if ( ! ( agent - > cc_params = ast_cc_config_params_init ( ) ) ) {
cc_unref ( agent , " Could not init agent config params. " ) ;
return NULL ;
}
ast_cc_copy_config_params ( agent - > cc_params , cc_params ) ;
if ( ! ( agent - > callbacks = find_agent_callbacks ( caller_chan ) ) ) {
cc_unref ( agent , " Could not find agent callbacks. " ) ;
return NULL ;
}
check_callback_sanity ( agent - > callbacks ) ;
if ( agent - > callbacks - > init ( agent , caller_chan ) ) {
cc_unref ( agent , " Agent init callback failed. " ) ;
return NULL ;
}
ast_log_dynamic_level ( cc_logger_level , " Core %d: Created an agent for caller %s \n " ,
agent - > core_id , agent - > device_name ) ;
return agent ;
}
/* Generic agent callbacks */
static int cc_generic_agent_init ( struct ast_cc_agent * agent , struct ast_channel * chan ) ;
static int cc_generic_agent_start_offer_timer ( struct ast_cc_agent * agent ) ;
static int cc_generic_agent_stop_offer_timer ( struct ast_cc_agent * agent ) ;
static void cc_generic_agent_ack ( struct ast_cc_agent * agent ) ;
static int cc_generic_agent_status_request ( struct ast_cc_agent * agent ) ;
static int cc_generic_agent_stop_ringing ( struct ast_cc_agent * agent ) ;
static int cc_generic_agent_start_monitoring ( struct ast_cc_agent * agent ) ;
static int cc_generic_agent_recall ( struct ast_cc_agent * agent ) ;
static void cc_generic_agent_destructor ( struct ast_cc_agent * agent ) ;
static struct ast_cc_agent_callbacks generic_agent_callbacks = {
. type = " generic " ,
. init = cc_generic_agent_init ,
. start_offer_timer = cc_generic_agent_start_offer_timer ,
. stop_offer_timer = cc_generic_agent_stop_offer_timer ,
. ack = cc_generic_agent_ack ,
. status_request = cc_generic_agent_status_request ,
. stop_ringing = cc_generic_agent_stop_ringing ,
. start_monitoring = cc_generic_agent_start_monitoring ,
. callee_available = cc_generic_agent_recall ,
. destructor = cc_generic_agent_destructor ,
} ;
struct cc_generic_agent_pvt {
/*!
* Subscription to device state
*
* Used in the CC_CALLER_BUSY state. The
* generic agent will subscribe to the
* device state of the caller in order to
* determine when we may move on
*/
struct ast_event_sub * sub ;
/*!
* Scheduler id of offer timer.
*/
int offer_timer_id ;
/*!
* Caller ID number
*
* When we re-call the caller, we need
* to provide this information to
* ast_request_and_dial so that the
* information will be present in the
* call to the callee
*/
char cid_num [ AST_CHANNEL_NAME ] ;
/*!
* Caller ID name
*
* See the description of cid_num.
* The same applies here, except this
* is the caller's name.
*/
char cid_name [ AST_CHANNEL_NAME ] ;
/*!
* Extension dialed
*
* The original extension dialed. This is used
* so that when performing a recall, we can
* call the proper extension.
*/
char exten [ AST_CHANNEL_NAME ] ;
/*!
* Context dialed
*
* The original context dialed. This is used
* so that when performaing a recall, we can
* call into the proper context
*/
char context [ AST_CHANNEL_NAME ] ;
} ;
static int cc_generic_agent_init ( struct ast_cc_agent * agent , struct ast_channel * chan )
{
struct cc_generic_agent_pvt * generic_pvt = ast_calloc ( 1 , sizeof ( * generic_pvt ) ) ;
if ( ! generic_pvt ) {
return - 1 ;
}
generic_pvt - > offer_timer_id = - 1 ;
2010-07-14 15:48:36 +00:00
if ( chan - > caller . id . number . valid & & chan - > caller . id . number . str ) {
ast_copy_string ( generic_pvt - > cid_num , chan - > caller . id . number . str , sizeof ( generic_pvt - > cid_num ) ) ;
2010-05-20 20:49:40 +00:00
}
2010-07-14 15:48:36 +00:00
if ( chan - > caller . id . name . valid & & chan - > caller . id . name . str ) {
ast_copy_string ( generic_pvt - > cid_name , chan - > caller . id . name . str , sizeof ( generic_pvt - > cid_name ) ) ;
2010-05-20 20:49:40 +00:00
}
2010-04-09 15:31:32 +00:00
ast_copy_string ( generic_pvt - > exten , S_OR ( chan - > macroexten , chan - > exten ) , sizeof ( generic_pvt - > exten ) ) ;
ast_copy_string ( generic_pvt - > context , S_OR ( chan - > macrocontext , chan - > context ) , sizeof ( generic_pvt - > context ) ) ;
agent - > private_data = generic_pvt ;
ast_set_flag ( agent , AST_CC_AGENT_SKIP_OFFER ) ;
return 0 ;
}
static int offer_timer_expire ( const void * data )
{
2010-04-27 22:11:58 +00:00
struct ast_cc_agent * agent = ( struct ast_cc_agent * ) data ;
2010-04-09 15:31:32 +00:00
struct cc_generic_agent_pvt * agent_pvt = agent - > private_data ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: Queuing change request because offer timer has expired. \n " ,
agent - > core_id ) ;
agent_pvt - > offer_timer_id = - 1 ;
ast_cc_failed ( agent - > core_id , " Generic agent %s offer timer expired " , agent - > device_name ) ;
2010-04-27 22:11:58 +00:00
cc_unref ( agent , " Remove scheduler's reference to the agent " ) ;
2010-04-09 15:31:32 +00:00
return 0 ;
}
static int cc_generic_agent_start_offer_timer ( struct ast_cc_agent * agent )
{
int when ;
int sched_id ;
struct cc_generic_agent_pvt * generic_pvt = agent - > private_data ;
ast_assert ( cc_sched_thread ! = NULL ) ;
ast_assert ( agent - > cc_params ! = NULL ) ;
when = ast_get_cc_offer_timer ( agent - > cc_params ) * 1000 ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: About to schedule offer timer expiration for %d ms \n " ,
agent - > core_id , when ) ;
if ( ( sched_id = ast_sched_thread_add ( cc_sched_thread , when , offer_timer_expire , cc_ref ( agent , " Give scheduler an agent ref " ) ) ) = = - 1 ) {
return - 1 ;
}
generic_pvt - > offer_timer_id = sched_id ;
return 0 ;
}
static int cc_generic_agent_stop_offer_timer ( struct ast_cc_agent * agent )
{
struct cc_generic_agent_pvt * generic_pvt = agent - > private_data ;
if ( generic_pvt - > offer_timer_id ! = - 1 ) {
if ( ! ast_sched_thread_del ( cc_sched_thread , generic_pvt - > offer_timer_id ) ) {
cc_unref ( agent , " Remove scheduler's reference to the agent " ) ;
}
generic_pvt - > offer_timer_id = - 1 ;
}
return 0 ;
}
static void cc_generic_agent_ack ( struct ast_cc_agent * agent )
{
/* The generic agent doesn't have to do anything special to
* acknowledge a CC request. Just return.
*/
return ;
}
static int cc_generic_agent_status_request ( struct ast_cc_agent * agent )
{
ast_cc_agent_status_response ( agent - > core_id , ast_device_state ( agent - > device_name ) ) ;
return 0 ;
}
static int cc_generic_agent_stop_ringing ( struct ast_cc_agent * agent )
{
struct ast_channel * recall_chan = ast_channel_get_by_name_prefix ( agent - > device_name , strlen ( agent - > device_name ) ) ;
if ( ! recall_chan ) {
return 0 ;
}
ast_softhangup ( recall_chan , AST_SOFTHANGUP_EXPLICIT ) ;
return 0 ;
}
static int generic_agent_devstate_unsubscribe ( void * data )
{
struct ast_cc_agent * agent = data ;
struct cc_generic_agent_pvt * generic_pvt = agent - > private_data ;
if ( generic_pvt - > sub ! = NULL ) {
generic_pvt - > sub = ast_event_unsubscribe ( generic_pvt - > sub ) ;
}
cc_unref ( agent , " Done unsubscribing from devstate " ) ;
return 0 ;
}
static void generic_agent_devstate_cb ( const struct ast_event * event , void * userdata )
{
struct ast_cc_agent * agent = userdata ;
/* We can't unsubscribe from device state events here because it causes a deadlock */
if ( ast_taskprocessor_push ( cc_core_taskprocessor , generic_agent_devstate_unsubscribe ,
cc_ref ( agent , " ref agent for device state unsubscription " ) ) ) {
cc_unref ( agent , " Unref agent unsubscribing from devstate failed " ) ;
}
ast_cc_agent_caller_available ( agent - > core_id , " %s is no longer busy " , agent - > device_name ) ;
}
static int cc_generic_agent_start_monitoring ( struct ast_cc_agent * agent )
{
struct cc_generic_agent_pvt * generic_pvt = agent - > private_data ;
struct ast_str * str = ast_str_alloca ( 128 ) ;
ast_assert ( generic_pvt - > sub = = NULL ) ;
ast_str_set ( & str , 0 , " Starting to monitor %s device state since it is busy \n " , agent - > device_name ) ;
if ( ! ( generic_pvt - > sub = ast_event_subscribe (
AST_EVENT_DEVICE_STATE , generic_agent_devstate_cb , ast_str_buffer ( str ) , agent ,
AST_EVENT_IE_DEVICE , AST_EVENT_IE_PLTYPE_STR , agent - > device_name ,
AST_EVENT_IE_STATE , AST_EVENT_IE_PLTYPE_UINT , AST_DEVICE_NOT_INUSE ,
AST_EVENT_IE_END ) ) ) {
return - 1 ;
}
return 0 ;
}
static void * generic_recall ( void * data )
{
struct ast_cc_agent * agent = data ;
struct cc_generic_agent_pvt * generic_pvt = agent - > private_data ;
const char * interface = S_OR ( ast_get_cc_agent_dialstring ( agent - > cc_params ) , ast_strdupa ( agent - > device_name ) ) ;
const char * tech ;
char * target ;
int reason ;
struct ast_channel * chan ;
const char * callback_macro = ast_get_cc_callback_macro ( agent - > cc_params ) ;
unsigned int recall_timer = ast_get_cc_recall_timer ( agent - > cc_params ) * 1000 ;
tech = interface ;
if ( ( target = strchr ( interface , ' / ' ) ) ) {
* target + + = ' \0 ' ;
}
if ( ! ( chan = ast_request_and_dial ( tech , AST_FORMAT_SLINEAR , NULL , target , recall_timer , & reason , generic_pvt - > cid_num , generic_pvt - > cid_name ) ) ) {
/* Hmm, no channel. Sucks for you, bud.
*/
ast_log_dynamic_level ( cc_logger_level , " Core %d: Failed to call back %s for reason %d \n " ,
agent - > core_id , agent - > device_name , reason ) ;
ast_cc_failed ( agent - > core_id , " Failed to call back device %s/%s " , tech , target ) ;
return NULL ;
}
if ( ! ast_strlen_zero ( callback_macro ) ) {
ast_log_dynamic_level ( cc_logger_level , " Core %d: There's a callback macro configured for agent %s \n " ,
agent - > core_id , agent - > device_name ) ;
if ( ast_app_run_macro ( NULL , chan , callback_macro , NULL ) ) {
ast_cc_failed ( agent - > core_id , " Callback macro to %s failed. Maybe a hangup? " , agent - > device_name ) ;
ast_hangup ( chan ) ;
return NULL ;
}
}
/* We have a channel. It's time now to set up the datastore of recalled CC interfaces.
* This will be a common task for all recall functions. If it were possible, I'd have
* the core do it automatically, but alas I cannot. Instead, I will provide a public
* function to do so.
*/
ast_setup_cc_recall_datastore ( chan , agent - > core_id ) ;
ast_cc_agent_set_interfaces_chanvar ( chan ) ;
ast_copy_string ( chan - > exten , generic_pvt - > exten , sizeof ( chan - > exten ) ) ;
ast_copy_string ( chan - > context , generic_pvt - > context , sizeof ( chan - > context ) ) ;
chan - > priority = 1 ;
ast_cc_agent_recalling ( agent - > core_id , " Generic agent %s is recalling " , agent - > device_name ) ;
ast_pbx_start ( chan ) ;
return NULL ;
}
static int cc_generic_agent_recall ( struct ast_cc_agent * agent )
{
pthread_t clotho ;
enum ast_device_state current_state = ast_device_state ( agent - > device_name ) ;
if ( current_state ! = AST_DEVICE_NOT_INUSE & & current_state ! = AST_DEVICE_UNKNOWN ) {
/* We can't try to contact the device right now because he's not available
* Let the core know he's busy.
*/
ast_cc_agent_caller_busy ( agent - > core_id , " Generic agent caller %s is busy " , agent - > device_name ) ;
return 0 ;
}
ast_pthread_create_detached_background ( & clotho , NULL , generic_recall , agent ) ;
return 0 ;
}
static void cc_generic_agent_destructor ( struct ast_cc_agent * agent )
{
struct cc_generic_agent_pvt * agent_pvt = agent - > private_data ;
if ( ! agent_pvt ) {
/* The agent constructor probably failed. */
return ;
}
cc_generic_agent_stop_offer_timer ( agent ) ;
if ( agent_pvt - > sub ) {
agent_pvt - > sub = ast_event_unsubscribe ( agent_pvt - > sub ) ;
}
ast_free ( agent_pvt ) ;
}
static void cc_core_instance_destructor ( void * data )
{
struct cc_core_instance * core_instance = data ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: Destroying core instance \n " , core_instance - > core_id ) ;
if ( core_instance - > agent ) {
cc_unref ( core_instance - > agent , " Core instance is done with the agent now " ) ;
}
if ( core_instance - > monitors ) {
core_instance - > monitors = cc_unref ( core_instance - > monitors , " Core instance is done with interface list " ) ;
}
}
static struct cc_core_instance * cc_core_init_instance ( struct ast_channel * caller_chan ,
struct cc_monitor_tree * called_tree , const int core_id , struct cc_control_payload * cc_data )
{
char caller [ AST_CHANNEL_NAME ] ;
struct cc_core_instance * core_instance ;
struct ast_cc_config_params * cc_params ;
long agent_count ;
int recall_core_id ;
ast_channel_get_device_name ( caller_chan , caller , sizeof ( caller ) ) ;
cc_params = ast_channel_get_cc_config_params ( caller_chan ) ;
if ( ! cc_params ) {
ast_log_dynamic_level ( cc_logger_level , " Could not get CC parameters for %s \n " ,
caller ) ;
return NULL ;
}
/* First, we need to kill off other pending CC offers from caller. If the caller is going
* to request a CC service, it may only be for the latest call he made.
*/
if ( ast_get_cc_agent_policy ( cc_params ) = = AST_CC_AGENT_GENERIC ) {
kill_duplicate_offers ( caller ) ;
}
ast_cc_is_recall ( caller_chan , & recall_core_id , NULL ) ;
agent_count = count_agents ( caller , recall_core_id ) ;
if ( agent_count > = ast_get_cc_max_agents ( cc_params ) ) {
ast_log_dynamic_level ( cc_logger_level , " Caller %s already has the maximum number of agents configured \n " , caller ) ;
return NULL ;
}
/* Generic agents can only have a single outstanding CC request per caller. */
if ( agent_count > 0 & & ast_get_cc_agent_policy ( cc_params ) = = AST_CC_AGENT_GENERIC ) {
ast_log_dynamic_level ( cc_logger_level , " Generic agents can only have a single outstanding request \n " ) ;
return NULL ;
}
/* Next, we need to create the core instance for this call */
if ( ! ( core_instance = ao2_t_alloc ( sizeof ( * core_instance ) , cc_core_instance_destructor , " Creating core instance for CC " ) ) ) {
return NULL ;
}
core_instance - > core_id = core_id ;
if ( ! ( core_instance - > agent = cc_agent_init ( caller_chan , caller , core_instance - > core_id , called_tree ) ) ) {
cc_unref ( core_instance , " Couldn't allocate agent, unref core_instance " ) ;
return NULL ;
}
core_instance - > monitors = cc_ref ( called_tree , " Core instance getting ref to monitor tree " ) ;
ao2_t_link ( cc_core_instances , core_instance , " Link core instance into container " ) ;
return core_instance ;
}
struct cc_state_change_args {
enum cc_state state ;
int core_id ;
char debug [ 1 ] ;
} ;
static int is_state_change_valid ( enum cc_state current_state , const enum cc_state new_state , struct ast_cc_agent * agent )
{
int is_valid = 0 ;
switch ( new_state ) {
case CC_AVAILABLE :
ast_log_dynamic_level ( cc_logger_level , " Core %d: Asked to change to state %d? That should never happen. \n " ,
agent - > core_id , new_state ) ;
break ;
case CC_CALLER_OFFERED :
if ( current_state = = CC_AVAILABLE ) {
is_valid = 1 ;
}
break ;
case CC_CALLER_REQUESTED :
if ( current_state = = CC_CALLER_OFFERED | |
( current_state = = CC_AVAILABLE & & ast_test_flag ( agent , AST_CC_AGENT_SKIP_OFFER ) ) ) {
is_valid = 1 ;
}
break ;
case CC_ACTIVE :
if ( current_state = = CC_CALLER_REQUESTED | | current_state = = CC_CALLER_BUSY ) {
is_valid = 1 ;
}
break ;
case CC_CALLEE_READY :
if ( current_state = = CC_ACTIVE ) {
is_valid = 1 ;
}
break ;
case CC_CALLER_BUSY :
if ( current_state = = CC_CALLEE_READY ) {
is_valid = 1 ;
}
break ;
case CC_RECALLING :
if ( current_state = = CC_CALLEE_READY ) {
is_valid = 1 ;
}
break ;
case CC_COMPLETE :
if ( current_state = = CC_RECALLING ) {
is_valid = 1 ;
}
break ;
case CC_FAILED :
is_valid = 1 ;
break ;
default :
ast_log_dynamic_level ( cc_logger_level , " Core %d: Asked to change to unknown state %d \n " ,
agent - > core_id , new_state ) ;
break ;
}
return is_valid ;
}
static int cc_available ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
/* This should never happen... */
ast_log ( LOG_WARNING , " Someone requested to change to CC_AVAILABLE? Ignoring. \n " ) ;
return - 1 ;
}
static int cc_caller_offered ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
if ( core_instance - > agent - > callbacks - > start_offer_timer ( core_instance - > agent ) ) {
ast_cc_failed ( core_instance - > core_id , " Failed to start the offer timer for %s \n " ,
core_instance - > agent - > device_name ) ;
return - 1 ;
}
manager_event ( EVENT_FLAG_CC , " CCOfferTimerStart " ,
" CoreID: %d \r \n "
" Caller: %s \r \n "
" Expires: %u \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name , core_instance - > agent - > cc_params - > cc_offer_timer ) ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: Started the offer timer for the agent %s! \n " ,
core_instance - > core_id , core_instance - > agent - > device_name ) ;
return 0 ;
}
/*!
* \brief check if the core instance has any device monitors
*
* In any case where we end up removing a device monitor from the
* list of device monitors, it is important to see what the state
* of the list is afterwards. If we find that we only have extension
* monitors left, then no devices are actually being monitored.
* In such a case, we need to declare that CC has failed for this
* call. This function helps those cases to determine if they should
* declare failure.
*
* \param core_instance The core instance we are checking for the existence
* of device monitors
* \retval 0 No device monitors exist on this core_instance
* \retval 1 There is still at least 1 device monitor remaining
*/
static int has_device_monitors ( struct cc_core_instance * core_instance )
{
struct ast_cc_monitor * iter ;
int res = 0 ;
AST_LIST_TRAVERSE ( core_instance - > monitors , iter , next ) {
if ( iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
res = 1 ;
break ;
}
}
return res ;
}
static void request_cc ( struct cc_core_instance * core_instance )
{
struct ast_cc_monitor * monitor_iter ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
if ( monitor_iter - > callbacks - > request_cc ( monitor_iter , & monitor_iter - > available_timer_id ) ) {
AST_LIST_REMOVE_CURRENT ( next ) ;
cc_extension_monitor_change_is_valid ( core_instance , monitor_iter - > parent_id ,
monitor_iter - > interface - > device_name , 1 ) ;
cc_unref ( monitor_iter , " request_cc failed. Unref list's reference to monitor " ) ;
} else {
manager_event ( EVENT_FLAG_CC , " CCRequested " ,
" CoreID: %d \r \n "
" Caller: %s \r \n "
" Callee: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name , monitor_iter - > interface - > device_name ) ;
}
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( ! has_device_monitors ( core_instance ) ) {
ast_cc_failed ( core_instance - > core_id , " All device monitors failed to request CC " ) ;
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
}
static int cc_caller_requested ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
if ( ! ast_cc_request_is_within_limits ( ) ) {
ast_log ( LOG_WARNING , " Cannot request CC since there is no more room for requests \n " ) ;
ast_cc_failed ( core_instance - > core_id , " Too many requests in the system " ) ;
return - 1 ;
}
core_instance - > agent - > callbacks - > stop_offer_timer ( core_instance - > agent ) ;
request_cc ( core_instance ) ;
return 0 ;
}
static void unsuspend ( struct cc_core_instance * core_instance )
{
struct ast_cc_monitor * monitor_iter ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
if ( monitor_iter - > callbacks - > unsuspend ( monitor_iter ) ) {
AST_LIST_REMOVE_CURRENT ( next ) ;
cc_extension_monitor_change_is_valid ( core_instance , monitor_iter - > parent_id ,
monitor_iter - > interface - > device_name , 1 ) ;
cc_unref ( monitor_iter , " unsuspend failed. Unref list's reference to monitor " ) ;
}
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( ! has_device_monitors ( core_instance ) ) {
ast_cc_failed ( core_instance - > core_id , " All device monitors failed to unsuspend CC " ) ;
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
}
static int cc_active ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
/* Either
* 1. Callee accepted CC request, call agent's ack callback.
* 2. Caller became available, call agent's stop_monitoring callback and
* call monitor's unsuspend callback.
*/
if ( previous_state = = CC_CALLER_REQUESTED ) {
core_instance - > agent - > callbacks - > ack ( core_instance - > agent ) ;
manager_event ( EVENT_FLAG_CC , " CCRequestAcknowledged " ,
" CoreID: %d \r \n "
" Caller: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name ) ;
} else if ( previous_state = = CC_CALLER_BUSY ) {
manager_event ( EVENT_FLAG_CC , " CCCallerStopMonitoring " ,
" CoreID: %d \r \n "
" Caller: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name ) ;
unsuspend ( core_instance ) ;
}
/* Not possible for previous_state to be anything else due to the is_state_change_valid check at the beginning */
return 0 ;
}
static int cc_callee_ready ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
core_instance - > agent - > callbacks - > callee_available ( core_instance - > agent ) ;
return 0 ;
}
static void suspend ( struct cc_core_instance * core_instance )
{
struct ast_cc_monitor * monitor_iter ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
if ( monitor_iter - > callbacks - > suspend ( monitor_iter ) ) {
AST_LIST_REMOVE_CURRENT ( next ) ;
cc_extension_monitor_change_is_valid ( core_instance , monitor_iter - > parent_id ,
monitor_iter - > interface - > device_name , 1 ) ;
cc_unref ( monitor_iter , " suspend failed. Unref list's reference to monitor " ) ;
}
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( ! has_device_monitors ( core_instance ) ) {
ast_cc_failed ( core_instance - > core_id , " All device monitors failed to suspend CC " ) ;
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
}
static int cc_caller_busy ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
/* Callee was available, but caller was busy, call agent's begin_monitoring callback
* and call monitor's suspend callback.
*/
suspend ( core_instance ) ;
core_instance - > agent - > callbacks - > start_monitoring ( core_instance - > agent ) ;
manager_event ( EVENT_FLAG_CC , " CCCallerStartMonitoring " ,
" CoreID: %d \r \n "
" Caller: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name ) ;
return 0 ;
}
static void cancel_available_timer ( struct cc_core_instance * core_instance )
{
struct ast_cc_monitor * monitor_iter ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
if ( monitor_iter - > callbacks - > cancel_available_timer ( monitor_iter , & monitor_iter - > available_timer_id ) ) {
AST_LIST_REMOVE_CURRENT ( next ) ;
cc_extension_monitor_change_is_valid ( core_instance , monitor_iter - > parent_id ,
monitor_iter - > interface - > device_name , 1 ) ;
cc_unref ( monitor_iter , " cancel_available_timer failed. Unref list's reference to monitor " ) ;
}
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( ! has_device_monitors ( core_instance ) ) {
ast_cc_failed ( core_instance - > core_id , " All device monitors failed to cancel their available timers " ) ;
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
}
static int cc_recalling ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
/* Both caller and callee are available, call agent's recall callback
*/
cancel_available_timer ( core_instance ) ;
manager_event ( EVENT_FLAG_CC , " CCCallerRecalling " ,
" CoreID: %d \r \n "
" Caller: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name ) ;
return 0 ;
}
static int cc_complete ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
/* Recall has made progress, call agent and monitor destructor functions
*/
manager_event ( EVENT_FLAG_CC , " CCRecallComplete " ,
" CoreID: %d \r \n "
" Caller: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name ) ;
ao2_t_unlink ( cc_core_instances , core_instance , " Unlink core instance since CC recall has completed " ) ;
return 0 ;
}
static int cc_failed ( struct cc_core_instance * core_instance , struct cc_state_change_args * args , enum cc_state previous_state )
{
manager_event ( EVENT_FLAG_CC , " CCFailure " ,
" CoreID: %d \r \n "
" Caller: %s \r \n "
" Reason: %s \r \n " ,
core_instance - > core_id , core_instance - > agent - > device_name , args - > debug ) ;
ao2_t_unlink ( cc_core_instances , core_instance , " Unlink core instance since CC failed " ) ;
return 0 ;
}
static int ( * const state_change_funcs [ ] ) ( struct cc_core_instance * , struct cc_state_change_args * , enum cc_state previous_state ) = {
[ CC_AVAILABLE ] = cc_available ,
[ CC_CALLER_OFFERED ] = cc_caller_offered ,
[ CC_CALLER_REQUESTED ] = cc_caller_requested ,
[ CC_ACTIVE ] = cc_active ,
[ CC_CALLEE_READY ] = cc_callee_ready ,
[ CC_CALLER_BUSY ] = cc_caller_busy ,
[ CC_RECALLING ] = cc_recalling ,
[ CC_COMPLETE ] = cc_complete ,
[ CC_FAILED ] = cc_failed ,
} ;
static int cc_do_state_change ( void * datap )
{
struct cc_state_change_args * args = datap ;
struct cc_core_instance * core_instance ;
enum cc_state previous_state ;
int res ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: State change to %d requested. Reason: %s \n " ,
args - > core_id , args - > state , args - > debug ) ;
if ( ! ( core_instance = find_cc_core_instance ( args - > core_id ) ) ) {
ast_log_dynamic_level ( cc_logger_level , " Core %d: Unable to find core instance. \n " , args - > core_id ) ;
ast_free ( args ) ;
return - 1 ;
}
if ( ! is_state_change_valid ( core_instance - > current_state , args - > state , core_instance - > agent ) ) {
ast_log_dynamic_level ( cc_logger_level , " Core %d: Invalid state change requested. Cannot go from %s to %s \n " ,
args - > core_id , cc_state_to_string ( core_instance - > current_state ) , cc_state_to_string ( args - > state ) ) ;
ast_free ( args ) ;
cc_unref ( core_instance , " Unref core instance from when it was found earlier " ) ;
return - 1 ;
}
/* We can change to the new state now. */
previous_state = core_instance - > current_state ;
core_instance - > current_state = args - > state ;
res = state_change_funcs [ core_instance - > current_state ] ( core_instance , args , previous_state ) ;
ast_free ( args ) ;
cc_unref ( core_instance , " Unref since state change has completed " ) ; /* From ao2_find */
return res ;
}
static int cc_request_state_change ( enum cc_state state , const int core_id , const char * debug , va_list ap )
{
int res ;
int debuglen ;
char dummy [ 1 ] ;
va_list aq ;
struct cc_state_change_args * args ;
/* This initial call to vsnprintf is simply to find what the
* size of the string needs to be
*/
va_copy ( aq , ap ) ;
/* We add 1 to the result since vsnprintf's return does not
* include the terminating null byte
*/
debuglen = vsnprintf ( dummy , sizeof ( dummy ) , debug , aq ) + 1 ;
va_end ( aq ) ;
if ( ! ( args = ast_calloc ( 1 , sizeof ( * args ) + debuglen ) ) ) {
return - 1 ;
}
args - > state = state ;
args - > core_id = core_id ;
vsnprintf ( args - > debug , debuglen , debug , ap ) ;
res = ast_taskprocessor_push ( cc_core_taskprocessor , cc_do_state_change , args ) ;
if ( res ) {
ast_free ( args ) ;
}
return res ;
}
struct cc_recall_ds_data {
int core_id ;
char ignore ;
char nested ;
struct cc_monitor_tree * interface_tree ;
} ;
static void * cc_recall_ds_duplicate ( void * data )
{
struct cc_recall_ds_data * old_data = data ;
struct cc_recall_ds_data * new_data = ast_calloc ( 1 , sizeof ( * new_data ) ) ;
if ( ! new_data ) {
return NULL ;
}
new_data - > interface_tree = cc_ref ( old_data - > interface_tree , " Bump refcount of monitor tree for recall datastore duplicate " ) ;
new_data - > core_id = old_data - > core_id ;
new_data - > nested = 1 ;
return new_data ;
}
static void cc_recall_ds_destroy ( void * data )
{
struct cc_recall_ds_data * recall_data = data ;
recall_data - > interface_tree = cc_unref ( recall_data - > interface_tree , " Unref recall monitor tree " ) ;
ast_free ( recall_data ) ;
}
static struct ast_datastore_info recall_ds_info = {
. type = " cc_recall " ,
. duplicate = cc_recall_ds_duplicate ,
. destroy = cc_recall_ds_destroy ,
} ;
int ast_setup_cc_recall_datastore ( struct ast_channel * chan , const int core_id )
{
struct ast_datastore * recall_datastore = ast_datastore_alloc ( & recall_ds_info , NULL ) ;
struct cc_recall_ds_data * recall_data ;
struct cc_core_instance * core_instance ;
if ( ! recall_datastore ) {
return - 1 ;
}
if ( ! ( recall_data = ast_calloc ( 1 , sizeof ( * recall_data ) ) ) ) {
ast_datastore_free ( recall_datastore ) ;
return - 1 ;
}
if ( ! ( core_instance = find_cc_core_instance ( core_id ) ) ) {
ast_free ( recall_data ) ;
ast_datastore_free ( recall_datastore ) ;
return - 1 ;
}
recall_data - > interface_tree = cc_ref ( core_instance - > monitors ,
" Bump refcount for monitor tree for recall datastore " ) ;
recall_data - > core_id = core_id ;
recall_datastore - > data = recall_data ;
recall_datastore - > inheritance = DATASTORE_INHERIT_FOREVER ;
ast_channel_lock ( chan ) ;
ast_channel_datastore_add ( chan , recall_datastore ) ;
ast_channel_unlock ( chan ) ;
cc_unref ( core_instance , " Recall datastore set up. No need for core_instance ref " ) ;
return 0 ;
}
int ast_cc_is_recall ( struct ast_channel * chan , int * core_id , const char * const monitor_type )
{
struct ast_datastore * recall_datastore ;
struct cc_recall_ds_data * recall_data ;
struct cc_monitor_tree * interface_tree ;
char device_name [ AST_CHANNEL_NAME ] ;
struct ast_cc_monitor * device_monitor ;
int core_id_candidate ;
ast_assert ( core_id ! = NULL ) ;
* core_id = - 1 ;
ast_channel_lock ( chan ) ;
if ( ! ( recall_datastore = ast_channel_datastore_find ( chan , & recall_ds_info , NULL ) ) ) {
/* Obviously not a recall if the datastore isn't present */
ast_channel_unlock ( chan ) ;
return 0 ;
}
recall_data = recall_datastore - > data ;
if ( recall_data - > ignore ) {
/* Though this is a recall, the call to this particular interface is not part of the
* recall either because this is a call forward or because this is not the first
* invocation of Dial during this call
*/
ast_channel_unlock ( chan ) ;
return 0 ;
}
if ( ! recall_data - > nested ) {
/* If the nested flag is not set, then this means that
* the channel passed to this function is the caller making
* the recall. This means that we shouldn't look through
* the monitor tree for the channel because it shouldn't be
* there. However, this is a recall though, so return true.
*/
* core_id = recall_data - > core_id ;
ast_channel_unlock ( chan ) ;
return 1 ;
}
if ( ast_strlen_zero ( monitor_type ) ) {
/* If someone passed a NULL or empty monitor type, then it is clear
* the channel they passed in was an incoming channel, and so searching
* the list of dialed interfaces is not going to be helpful. Just return
* false immediately.
*/
ast_channel_unlock ( chan ) ;
return 0 ;
}
interface_tree = recall_data - > interface_tree ;
ast_channel_get_device_name ( chan , device_name , sizeof ( device_name ) ) ;
/* We grab the value of the recall_data->core_id so that we
* can unlock the channel before we start looking through the
* interface list. That way we don't have to worry about a possible
* clash between the channel lock and the monitor tree lock.
*/
core_id_candidate = recall_data - > core_id ;
ast_channel_unlock ( chan ) ;
/*
* Now we need to find out if the channel device name
* is in the list of interfaces in the called tree.
*/
AST_LIST_LOCK ( interface_tree ) ;
AST_LIST_TRAVERSE ( interface_tree , device_monitor , next ) {
if ( ! strcmp ( device_monitor - > interface - > device_name , device_name ) & &
! strcmp ( device_monitor - > interface - > monitor_type , monitor_type ) ) {
/* BOOM! Device is in the tree! We have a winner! */
* core_id = core_id_candidate ;
AST_LIST_UNLOCK ( interface_tree ) ;
return 1 ;
}
}
AST_LIST_UNLOCK ( interface_tree ) ;
return 0 ;
}
struct ast_cc_monitor * ast_cc_get_monitor_by_recall_core_id ( const int core_id , const char * const device_name )
{
struct cc_core_instance * core_instance = find_cc_core_instance ( core_id ) ;
struct ast_cc_monitor * monitor_iter ;
if ( ! core_instance ) {
return NULL ;
}
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE ( core_instance - > monitors , monitor_iter , next ) {
if ( ! strcmp ( monitor_iter - > interface - > device_name , device_name ) ) {
/* Found a monitor. */
cc_ref ( monitor_iter , " Hand the requester of the monitor a reference " ) ;
break ;
}
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
cc_unref ( core_instance , " Done with core instance ref in ast_cc_get_monitor_by_recall_core_id " ) ;
return monitor_iter ;
}
/*!
* \internal
* \brief uniquely append a dialstring to our CC_INTERFACES chanvar string.
*
* We will only append a string if it has not already appeared in our channel
* variable earlier. We ensure that we don't erroneously match substrings by
* adding an ampersand to the end of our potential dialstring and searching for
* it plus the ampersand in our variable.
*
* It's important to note that once we have built the full CC_INTERFACES string,
* there will be an extra ampersand at the end which must be stripped off by
* the caller of this function.
*
* \param str An ast_str holding what we will add to CC_INTERFACES
* \param dialstring A new dialstring to add
* \retval void
*/
static void cc_unique_append ( struct ast_str * str , const char * const dialstring )
{
char dialstring_search [ AST_CHANNEL_NAME ] ;
snprintf ( dialstring_search , sizeof ( dialstring_search ) , " %s%c " , dialstring , ' & ' ) ;
if ( strstr ( ast_str_buffer ( str ) , dialstring_search ) ) {
return ;
}
ast_str_append ( & str , 0 , " %s " , dialstring_search ) ;
}
/*!
* \internal
* \brief Build the CC_INTERFACES channel variable
*
* The method used is to traverse the child dialstrings in the
* passed-in extension monitor, adding any that have the is_valid
* flag set. Then, traverse the monitors, finding all children
* of the starting extension monitor and adding their dialstrings
* as well.
*
* \param starting_point The extension monitor that is the parent to all
* monitors whose dialstrings should be added to CC_INTERFACES
* \param str Where we will store CC_INTERFACES
* \retval void
*/
static void build_cc_interfaces_chanvar ( struct ast_cc_monitor * starting_point , struct ast_str * str )
{
struct extension_monitor_pvt * extension_pvt ;
struct extension_child_dialstring * child_dialstring ;
struct ast_cc_monitor * monitor_iter = starting_point ;
int top_level_id = starting_point - > id ;
/* First we need to take all of the is_valid child_dialstrings from
* the extension monitor we found and add them to the CC_INTERFACES
* chanvar
*/
extension_pvt = starting_point - > private_data ;
AST_LIST_TRAVERSE ( & extension_pvt - > child_dialstrings , child_dialstring , next ) {
if ( child_dialstring - > is_valid ) {
cc_unique_append ( str , child_dialstring - > original_dialstring ) ;
}
}
/* And now we get the dialstrings from each of the device monitors */
while ( ( monitor_iter = AST_LIST_NEXT ( monitor_iter , next ) ) ) {
if ( monitor_iter - > parent_id = = top_level_id ) {
cc_unique_append ( str , monitor_iter - > dialstring ) ;
}
}
/* str will have an extra '&' tacked onto the end of it, so we need
* to get rid of that.
*/
ast_str_truncate ( str , ast_str_strlen ( str ) - 1 ) ;
}
int ast_cc_agent_set_interfaces_chanvar ( struct ast_channel * chan )
{
struct ast_datastore * recall_datastore ;
struct cc_monitor_tree * interface_tree ;
struct ast_cc_monitor * monitor ;
struct cc_recall_ds_data * recall_data ;
struct ast_str * str = ast_str_create ( 64 ) ;
int core_id ;
if ( ! str ) {
return - 1 ;
}
ast_channel_lock ( chan ) ;
if ( ! ( recall_datastore = ast_channel_datastore_find ( chan , & recall_ds_info , NULL ) ) ) {
ast_channel_unlock ( chan ) ;
ast_free ( str ) ;
return - 1 ;
}
recall_data = recall_datastore - > data ;
interface_tree = recall_data - > interface_tree ;
core_id = recall_data - > core_id ;
ast_channel_unlock ( chan ) ;
AST_LIST_LOCK ( interface_tree ) ;
monitor = AST_LIST_FIRST ( interface_tree ) ;
build_cc_interfaces_chanvar ( monitor , str ) ;
AST_LIST_UNLOCK ( interface_tree ) ;
pbx_builtin_setvar_helper ( chan , " CC_INTERFACES " , ast_str_buffer ( str ) ) ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: CC_INTERFACES set to %s \n " ,
core_id , ast_str_buffer ( str ) ) ;
ast_free ( str ) ;
return 0 ;
}
int ast_set_cc_interfaces_chanvar ( struct ast_channel * chan , const char * const extension )
{
struct ast_datastore * recall_datastore ;
struct cc_monitor_tree * interface_tree ;
struct ast_cc_monitor * monitor_iter ;
struct cc_recall_ds_data * recall_data ;
struct ast_str * str = ast_str_create ( 64 ) ;
int core_id ;
if ( ! str ) {
return - 1 ;
}
ast_channel_lock ( chan ) ;
if ( ! ( recall_datastore = ast_channel_datastore_find ( chan , & recall_ds_info , NULL ) ) ) {
ast_channel_unlock ( chan ) ;
ast_free ( str ) ;
return - 1 ;
}
recall_data = recall_datastore - > data ;
interface_tree = recall_data - > interface_tree ;
core_id = recall_data - > core_id ;
ast_channel_unlock ( chan ) ;
AST_LIST_LOCK ( interface_tree ) ;
AST_LIST_TRAVERSE ( interface_tree , monitor_iter , next ) {
if ( ! strcmp ( monitor_iter - > interface - > device_name , extension ) ) {
break ;
}
}
if ( ! monitor_iter ) {
/* We couldn't find this extension. This may be because
* we have been directed into an unexpected extension because
* the admin has changed a CC_INTERFACES variable at some point.
*/
AST_LIST_UNLOCK ( interface_tree ) ;
ast_free ( str ) ;
return - 1 ;
}
build_cc_interfaces_chanvar ( monitor_iter , str ) ;
AST_LIST_UNLOCK ( interface_tree ) ;
pbx_builtin_setvar_helper ( chan , " CC_INTERFACES " , ast_str_buffer ( str ) ) ;
ast_log_dynamic_level ( cc_logger_level , " Core %d: CC_INTERFACES set to %s \n " ,
core_id , ast_str_buffer ( str ) ) ;
ast_free ( str ) ;
return 0 ;
}
void ast_ignore_cc ( struct ast_channel * chan )
{
struct ast_datastore * cc_datastore ;
struct ast_datastore * cc_recall_datastore ;
struct dialed_cc_interfaces * cc_interfaces ;
struct cc_recall_ds_data * recall_cc_data ;
ast_channel_lock ( chan ) ;
if ( ( cc_datastore = ast_channel_datastore_find ( chan , & dialed_cc_interfaces_info , NULL ) ) ) {
cc_interfaces = cc_datastore - > data ;
cc_interfaces - > ignore = 1 ;
}
if ( ( cc_recall_datastore = ast_channel_datastore_find ( chan , & recall_ds_info , NULL ) ) ) {
recall_cc_data = cc_recall_datastore - > data ;
recall_cc_data - > ignore = 1 ;
}
ast_channel_unlock ( chan ) ;
}
static __attribute__ ( ( format ( printf , 2 , 3 ) ) ) int cc_offer ( const int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_CALLER_OFFERED , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_offer ( struct ast_channel * caller_chan )
{
int core_id ;
int res = - 1 ;
struct ast_datastore * datastore ;
struct dialed_cc_interfaces * cc_interfaces ;
char cc_is_offerable ;
ast_channel_lock ( caller_chan ) ;
if ( ! ( datastore = ast_channel_datastore_find ( caller_chan , & dialed_cc_interfaces_info , NULL ) ) ) {
ast_channel_unlock ( caller_chan ) ;
return res ;
}
cc_interfaces = datastore - > data ;
cc_is_offerable = cc_interfaces - > is_original_caller ;
core_id = cc_interfaces - > core_id ;
ast_channel_unlock ( caller_chan ) ;
if ( cc_is_offerable ) {
res = cc_offer ( core_id , " CC offered to caller %s " , caller_chan - > name ) ;
}
return res ;
}
int ast_cc_agent_accept_request ( int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_CALLER_REQUESTED , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_monitor_request_acked ( int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_ACTIVE , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_monitor_callee_available ( const int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_CALLEE_READY , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_agent_caller_busy ( int core_id , const char * debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_CALLER_BUSY , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_agent_caller_available ( int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_ACTIVE , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_agent_recalling ( int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_RECALLING , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_completed ( struct ast_channel * chan , const char * const debug , . . . )
{
struct ast_datastore * recall_datastore ;
struct cc_recall_ds_data * recall_data ;
int core_id ;
va_list ap ;
int res ;
ast_channel_lock ( chan ) ;
if ( ! ( recall_datastore = ast_channel_datastore_find ( chan , & recall_ds_info , NULL ) ) ) {
/* Silly! Why did you call this function if there's no recall DS? */
ast_channel_unlock ( chan ) ;
return - 1 ;
}
recall_data = recall_datastore - > data ;
if ( recall_data - > nested | | recall_data - > ignore ) {
/* If this is being called from a nested Dial, it is too
* early to determine if the recall has actually completed.
* The outermost dial is the only one with the authority to
* declare the recall to be complete.
*
* Similarly, if this function has been called when the
* recall has progressed beyond the first dial, this is not
* a legitimate time to declare the recall to be done. In fact,
* that should have been done already.
*/
ast_channel_unlock ( chan ) ;
return - 1 ;
}
core_id = recall_data - > core_id ;
ast_channel_unlock ( chan ) ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_COMPLETE , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
int ast_cc_failed ( int core_id , const char * const debug , . . . )
{
va_list ap ;
int res ;
va_start ( ap , debug ) ;
res = cc_request_state_change ( CC_FAILED , core_id , debug , ap ) ;
va_end ( ap ) ;
return res ;
}
struct ast_cc_monitor_failure_data {
const char * device_name ;
char * debug ;
int core_id ;
} ;
static int cc_monitor_failed ( void * data )
{
struct ast_cc_monitor_failure_data * failure_data = data ;
struct cc_core_instance * core_instance ;
struct ast_cc_monitor * monitor_iter ;
core_instance = find_cc_core_instance ( failure_data - > core_id ) ;
if ( ! core_instance ) {
/* Core instance no longer exists or invalid core_id. */
ast_log_dynamic_level ( cc_logger_level ,
" Core %d: Could not find core instance for device %s '%s' \n " ,
failure_data - > core_id , failure_data - > device_name , failure_data - > debug ) ;
ast_free ( ( char * ) failure_data - > device_name ) ;
ast_free ( ( char * ) failure_data - > debug ) ;
ast_free ( failure_data ) ;
return - 1 ;
}
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
if ( ! strcmp ( monitor_iter - > interface - > device_name , failure_data - > device_name ) ) {
AST_LIST_REMOVE_CURRENT ( next ) ;
cc_extension_monitor_change_is_valid ( core_instance , monitor_iter - > parent_id ,
monitor_iter - > interface - > device_name , 1 ) ;
monitor_iter - > callbacks - > cancel_available_timer ( monitor_iter , & monitor_iter - > available_timer_id ) ;
manager_event ( EVENT_FLAG_CC , " CCMonitorFailed " ,
" CoreID: %d \r \n "
" Callee: %s \r \n " ,
monitor_iter - > core_id , monitor_iter - > interface - > device_name ) ;
cc_unref ( monitor_iter , " Monitor reported failure. Unref list's reference. " ) ;
}
}
}
AST_LIST_TRAVERSE_SAFE_END ;
if ( ! has_device_monitors ( core_instance ) ) {
ast_cc_failed ( core_instance - > core_id , " All monitors have failed \n " ) ;
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
cc_unref ( core_instance , " Finished with core_instance in cc_monitor_failed \n " ) ;
ast_free ( ( char * ) failure_data - > device_name ) ;
ast_free ( ( char * ) failure_data - > debug ) ;
ast_free ( failure_data ) ;
return 0 ;
}
int ast_cc_monitor_failed ( int core_id , const char * const monitor_name , const char * const debug , . . . )
{
struct ast_cc_monitor_failure_data * failure_data ;
int res ;
va_list ap ;
if ( ! ( failure_data = ast_calloc ( 1 , sizeof ( * failure_data ) ) ) ) {
return - 1 ;
}
if ( ! ( failure_data - > device_name = ast_strdup ( monitor_name ) ) ) {
ast_free ( failure_data ) ;
return - 1 ;
}
va_start ( ap , debug ) ;
if ( ast_vasprintf ( & failure_data - > debug , debug , ap ) = = - 1 ) {
va_end ( ap ) ;
ast_free ( ( char * ) failure_data - > device_name ) ;
ast_free ( failure_data ) ;
return - 1 ;
}
va_end ( ap ) ;
failure_data - > core_id = core_id ;
res = ast_taskprocessor_push ( cc_core_taskprocessor , cc_monitor_failed , failure_data ) ;
if ( res ) {
ast_free ( ( char * ) failure_data - > device_name ) ;
ast_free ( ( char * ) failure_data - > debug ) ;
ast_free ( failure_data ) ;
}
return res ;
}
static int cc_status_request ( void * data )
{
struct cc_core_instance * core_instance = data ;
int res ;
res = core_instance - > agent - > callbacks - > status_request ( core_instance - > agent ) ;
cc_unref ( core_instance , " Status request finished. Unref core instance " ) ;
return res ;
}
int ast_cc_monitor_status_request ( int core_id )
{
int res ;
struct cc_core_instance * core_instance = find_cc_core_instance ( core_id ) ;
if ( ! core_instance ) {
return - 1 ;
}
res = ast_taskprocessor_push ( cc_core_taskprocessor , cc_status_request , core_instance ) ;
if ( res ) {
cc_unref ( core_instance , " Unref core instance. ast_taskprocessor_push failed " ) ;
}
return res ;
}
static int cc_stop_ringing ( void * data )
{
struct cc_core_instance * core_instance = data ;
int res = 0 ;
if ( core_instance - > agent - > callbacks - > stop_ringing ) {
res = core_instance - > agent - > callbacks - > stop_ringing ( core_instance - > agent ) ;
}
/* If an agent is being asked to stop ringing, then he needs to be prepared if for
* whatever reason he needs to be called back again. The proper state to be in to
* detect such a circumstance is the CC_ACTIVE state.
*
* We get to this state using the slightly unintuitive method of calling
* ast_cc_monitor_request_acked because it gets us to the proper state.
*/
ast_cc_monitor_request_acked ( core_instance - > core_id , " Agent %s asked to stop ringing. Be prepared to be recalled again. " ,
core_instance - > agent - > device_name ) ;
cc_unref ( core_instance , " Stop ringing finished. Unref core_instance " ) ;
return res ;
}
int ast_cc_monitor_stop_ringing ( int core_id )
{
int res ;
struct cc_core_instance * core_instance = find_cc_core_instance ( core_id ) ;
if ( ! core_instance ) {
return - 1 ;
}
res = ast_taskprocessor_push ( cc_core_taskprocessor , cc_stop_ringing , core_instance ) ;
if ( res ) {
cc_unref ( core_instance , " Unref core instance. ast_taskprocessor_push failed " ) ;
}
return res ;
}
static int cc_party_b_free ( void * data )
{
struct cc_core_instance * core_instance = data ;
int res = 0 ;
if ( core_instance - > agent - > callbacks - > party_b_free ) {
res = core_instance - > agent - > callbacks - > party_b_free ( core_instance - > agent ) ;
}
cc_unref ( core_instance , " Party B free finished. Unref core_instance " ) ;
return res ;
}
int ast_cc_monitor_party_b_free ( int core_id )
{
int res ;
struct cc_core_instance * core_instance = find_cc_core_instance ( core_id ) ;
if ( ! core_instance ) {
return - 1 ;
}
res = ast_taskprocessor_push ( cc_core_taskprocessor , cc_party_b_free , core_instance ) ;
if ( res ) {
cc_unref ( core_instance , " Unref core instance. ast_taskprocessor_push failed " ) ;
}
return res ;
}
struct cc_status_response_args {
struct cc_core_instance * core_instance ;
enum ast_device_state devstate ;
} ;
static int cc_status_response ( void * data )
{
struct cc_status_response_args * args = data ;
struct cc_core_instance * core_instance = args - > core_instance ;
struct ast_cc_monitor * monitor_iter ;
enum ast_device_state devstate = args - > devstate ;
ast_free ( args ) ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE ( core_instance - > monitors , monitor_iter , next ) {
if ( monitor_iter - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR & &
monitor_iter - > callbacks - > status_response ) {
monitor_iter - > callbacks - > status_response ( monitor_iter , devstate ) ;
}
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
cc_unref ( core_instance , " Status response finished. Unref core instance " ) ;
return 0 ;
}
int ast_cc_agent_status_response ( int core_id , enum ast_device_state devstate )
{
struct cc_status_response_args * args ;
struct cc_core_instance * core_instance ;
int res ;
args = ast_calloc ( 1 , sizeof ( * args ) ) ;
if ( ! args ) {
return - 1 ;
}
core_instance = find_cc_core_instance ( core_id ) ;
if ( ! core_instance ) {
ast_free ( args ) ;
return - 1 ;
}
args - > core_instance = core_instance ;
args - > devstate = devstate ;
res = ast_taskprocessor_push ( cc_core_taskprocessor , cc_status_response , args ) ;
if ( res ) {
cc_unref ( core_instance , " Unref core instance. ast_taskprocessor_push failed " ) ;
ast_free ( args ) ;
}
return res ;
}
static int cc_build_payload ( struct ast_channel * chan , struct ast_cc_config_params * cc_params ,
const char * monitor_type , const char * const device_name , const char * dialstring ,
enum ast_cc_service_type service , void * private_data , struct cc_control_payload * payload )
{
struct ast_datastore * datastore ;
struct dialed_cc_interfaces * cc_interfaces ;
int dial_parent_id ;
ast_channel_lock ( chan ) ;
datastore = ast_channel_datastore_find ( chan , & dialed_cc_interfaces_info , NULL ) ;
if ( ! datastore ) {
ast_channel_unlock ( chan ) ;
return - 1 ;
}
cc_interfaces = datastore - > data ;
dial_parent_id = cc_interfaces - > dial_parent_id ;
ast_channel_unlock ( chan ) ;
payload - > monitor_type = monitor_type ;
payload - > private_data = private_data ;
payload - > service = service ;
ast_cc_copy_config_params ( & payload - > config_params , cc_params ) ;
payload - > parent_interface_id = dial_parent_id ;
ast_copy_string ( payload - > device_name , device_name , sizeof ( payload - > device_name ) ) ;
ast_copy_string ( payload - > dialstring , dialstring , sizeof ( payload - > dialstring ) ) ;
return 0 ;
}
int ast_queue_cc_frame ( struct ast_channel * chan , const char * monitor_type ,
const char * const dialstring , enum ast_cc_service_type service , void * private_data )
{
struct ast_frame frame = { 0 , } ;
char device_name [ AST_CHANNEL_NAME ] ;
int retval ;
struct ast_cc_config_params * cc_params ;
cc_params = ast_channel_get_cc_config_params ( chan ) ;
if ( ! cc_params ) {
return - 1 ;
}
ast_channel_get_device_name ( chan , device_name , sizeof ( device_name ) ) ;
if ( ast_cc_monitor_count ( device_name , monitor_type ) > = ast_get_cc_max_monitors ( cc_params ) ) {
ast_log ( LOG_NOTICE , " Not queuing a CC frame for device %s since it already has its maximum monitors allocated \n " , device_name ) ;
return - 1 ;
}
if ( ast_cc_build_frame ( chan , cc_params , monitor_type , device_name , dialstring , service , private_data , & frame ) ) {
/* Frame building failed. We can't use this. */
return - 1 ;
}
retval = ast_queue_frame ( chan , & frame ) ;
ast_frfree ( & frame ) ;
return retval ;
}
int ast_cc_build_frame ( struct ast_channel * chan , struct ast_cc_config_params * cc_params ,
const char * monitor_type , const char * const device_name ,
const char * const dialstring , enum ast_cc_service_type service , void * private_data ,
struct ast_frame * frame )
{
struct cc_control_payload * payload = ast_calloc ( 1 , sizeof ( * payload ) ) ;
if ( ! payload ) {
return - 1 ;
}
if ( cc_build_payload ( chan , cc_params , monitor_type , device_name , dialstring , service , private_data , payload ) ) {
/* Something screwed up, we can't make a frame with this */
ast_free ( payload ) ;
return - 1 ;
}
frame - > frametype = AST_FRAME_CONTROL ;
frame - > subclass . integer = AST_CONTROL_CC ;
frame - > data . ptr = payload ;
frame - > datalen = sizeof ( * payload ) ;
frame - > mallocd = AST_MALLOCD_DATA ;
return 0 ;
}
void ast_cc_call_failed ( struct ast_channel * incoming , struct ast_channel * outgoing , const char * const dialstring )
{
char device_name [ AST_CHANNEL_NAME ] ;
struct cc_control_payload payload ;
struct ast_cc_config_params * cc_params ;
if ( outgoing - > hangupcause ! = AST_CAUSE_BUSY & & outgoing - > hangupcause ! = AST_CAUSE_CONGESTION ) {
/* It doesn't make sense to try to offer CCBS to the caller if the reason for ast_call
* failing is something other than busy or congestion
*/
return ;
}
cc_params = ast_channel_get_cc_config_params ( outgoing ) ;
if ( ! cc_params ) {
return ;
}
if ( ast_get_cc_monitor_policy ( cc_params ) ! = AST_CC_MONITOR_GENERIC ) {
/* This sort of CCBS only works if using generic CC. For native, we would end up sending
* a CC request for a non-existent call. The far end will reject this every time
*/
return ;
}
ast_channel_get_device_name ( outgoing , device_name , sizeof ( device_name ) ) ;
if ( cc_build_payload ( outgoing , cc_params , AST_CC_GENERIC_MONITOR_TYPE , device_name ,
dialstring , AST_CC_CCBS , NULL , & payload ) ) {
/* Something screwed up, we can't make a frame with this */
return ;
}
ast_handle_cc_control_frame ( incoming , outgoing , & payload ) ;
}
void ast_cc_busy_interface ( struct ast_channel * inbound , struct ast_cc_config_params * cc_params ,
const char * monitor_type , const char * const device_name , const char * const dialstring , void * private_data )
{
struct cc_control_payload payload ;
if ( cc_build_payload ( inbound , cc_params , monitor_type , device_name , dialstring , AST_CC_CCBS , private_data , & payload ) ) {
/* Something screwed up. Don't try to handle this payload */
call_destructor_with_no_monitor ( monitor_type , private_data ) ;
return ;
}
ast_handle_cc_control_frame ( inbound , NULL , & payload ) ;
}
int ast_cc_callback ( struct ast_channel * inbound , const char * const tech , const char * const dest , ast_cc_callback_fn callback )
{
const struct ast_channel_tech * chantech = ast_get_channel_tech ( tech ) ;
if ( chantech & & chantech - > cc_callback ) {
chantech - > cc_callback ( inbound , dest , callback ) ;
}
return 0 ;
}
static const char * ccreq_app = " CallCompletionRequest " ;
static int ccreq_exec ( struct ast_channel * chan , const char * data )
{
struct cc_core_instance * core_instance ;
char device_name [ AST_CHANNEL_NAME ] ;
unsigned long match_flags ;
int res ;
ast_channel_get_device_name ( chan , device_name , sizeof ( device_name ) ) ;
match_flags = MATCH_NO_REQUEST ;
if ( ! ( core_instance = ao2_t_callback_data ( cc_core_instances , 0 , match_agent , device_name , & match_flags , " Find core instance for CallCompletionRequest " ) ) ) {
ast_log_dynamic_level ( cc_logger_level , " Couldn't find a core instance for caller %s \n " , device_name ) ;
return - 1 ;
}
ast_log_dynamic_level ( cc_logger_level , " Core %d: Found core_instance for caller %s \n " ,
core_instance - > core_id , device_name ) ;
if ( strcmp ( core_instance - > agent - > callbacks - > type , " generic " ) ) {
ast_log_dynamic_level ( cc_logger_level , " Core %d: CallCompletionRequest is only for generic agent types. \n " ,
core_instance - > core_id ) ;
pbx_builtin_setvar_helper ( chan , " CC_REQUEST_RESULT " , " FAIL " ) ;
cc_unref ( core_instance , " Unref core_instance since CallCompletionRequest was called with native agent " ) ;
return 0 ;
}
if ( ! ast_cc_request_is_within_limits ( ) ) {
ast_log_dynamic_level ( cc_logger_level , " Core %d: CallCompletionRequest failed. Too many requests in the system \n " ,
core_instance - > core_id ) ;
ast_cc_failed ( core_instance - > core_id , " Too many CC requests \n " ) ;
pbx_builtin_setvar_helper ( chan , " CC_REQUEST_RESULT " , " FAIL " ) ;
cc_unref ( core_instance , " Unref core_instance since too many CC requests " ) ;
return 0 ;
}
res = ast_cc_agent_accept_request ( core_instance - > core_id , " CallCompletionRequest called by caller %s for core_id %d " , device_name , core_instance - > core_id ) ;
pbx_builtin_setvar_helper ( chan , " CC_REQUEST_RESULT " , res ? " FAIL " : " SUCCESS " ) ;
cc_unref ( core_instance , " Done with CallCompletionRequest " ) ;
return res ;
}
static const char * cccancel_app = " CallCompletionCancel " ;
static int cccancel_exec ( struct ast_channel * chan , const char * data )
{
struct cc_core_instance * core_instance ;
char device_name [ AST_CHANNEL_NAME ] ;
unsigned long match_flags ;
int res ;
ast_channel_get_device_name ( chan , device_name , sizeof ( device_name ) ) ;
match_flags = MATCH_REQUEST ;
if ( ! ( core_instance = ao2_t_callback_data ( cc_core_instances , 0 , match_agent , device_name , & match_flags , " Find core instance for CallCompletionCancel " ) ) ) {
ast_log ( LOG_WARNING , " Cannot find CC transaction to cancel for caller %s \n " , device_name ) ;
return - 1 ;
}
if ( strcmp ( core_instance - > agent - > callbacks - > type , " generic " ) ) {
ast_log ( LOG_WARNING , " CallCompletionCancel may only be used for calles with a generic agent \n " ) ;
cc_unref ( core_instance , " Unref core instance found during CallCompletionCancel " ) ;
return - 1 ;
}
res = ast_cc_failed ( core_instance - > core_id , " Call completion request Cancelled for core ID %d by caller %s " ,
core_instance - > core_id , device_name ) ;
cc_unref ( core_instance , " Unref core instance found during CallCompletionCancel " ) ;
return res ;
}
struct count_monitors_cb_data {
const char * device_name ;
const char * monitor_type ;
int count ;
} ;
static int count_monitors_cb ( void * obj , void * arg , int flags )
{
struct cc_core_instance * core_instance = obj ;
struct count_monitors_cb_data * cb_data = arg ;
const char * device_name = cb_data - > device_name ;
const char * monitor_type = cb_data - > monitor_type ;
struct ast_cc_monitor * monitor_iter ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
AST_LIST_TRAVERSE ( core_instance - > monitors , monitor_iter , next ) {
if ( ! strcmp ( monitor_iter - > interface - > device_name , device_name ) & &
! strcmp ( monitor_iter - > interface - > monitor_type , monitor_type ) ) {
cb_data - > count + + ;
break ;
}
}
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
return 0 ;
}
int ast_cc_monitor_count ( const char * const name , const char * const type )
{
struct count_monitors_cb_data data = { . device_name = name , . monitor_type = type , } ;
ao2_t_callback ( cc_core_instances , OBJ_NODATA , count_monitors_cb , & data , " Counting agents " ) ;
ast_log_dynamic_level ( cc_logger_level , " Counted %d monitors \n " , data . count ) ;
return data . count ;
}
static void initialize_cc_max_requests ( void )
{
struct ast_config * cc_config ;
const char * cc_max_requests_str ;
struct ast_flags config_flags = { 0 , } ;
char * endptr ;
cc_config = ast_config_load2 ( " ccss.conf " , " ccss " , config_flags ) ;
if ( ! cc_config | | cc_config = = CONFIG_STATUS_FILEINVALID ) {
ast_log ( LOG_WARNING , " Could not find valid ccss.conf file. Using cc_max_requests default \n " ) ;
global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT ;
return ;
}
if ( ! ( cc_max_requests_str = ast_variable_retrieve ( cc_config , " general " , " cc_max_requests " ) ) ) {
ast_config_destroy ( cc_config ) ;
global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT ;
return ;
}
global_cc_max_requests = strtol ( cc_max_requests_str , & endptr , 10 ) ;
if ( ! ast_strlen_zero ( endptr ) ) {
ast_log ( LOG_WARNING , " Invalid input given for cc_max_requests. Using default \n " ) ;
global_cc_max_requests = GLOBAL_CC_MAX_REQUESTS_DEFAULT ;
}
ast_config_destroy ( cc_config ) ;
return ;
}
static void cc_cli_print_monitor_stats ( struct ast_cc_monitor * monitor , int fd , int parent_id )
{
struct ast_cc_monitor * child_monitor_iter = monitor ;
if ( ! monitor ) {
return ;
}
ast_cli ( fd , " \t \t |-->%s " , monitor - > interface - > device_name ) ;
if ( monitor - > interface - > monitor_class = = AST_CC_DEVICE_MONITOR ) {
ast_cli ( fd , " (%s) " , cc_service_to_string ( monitor - > service_offered ) ) ;
}
ast_cli ( fd , " \n " ) ;
while ( ( child_monitor_iter = AST_LIST_NEXT ( child_monitor_iter , next ) ) ) {
if ( child_monitor_iter - > parent_id = = monitor - > id ) {
cc_cli_print_monitor_stats ( child_monitor_iter , fd , child_monitor_iter - > id ) ;
}
}
}
static int print_stats_cb ( void * obj , void * arg , int flags )
{
int * cli_fd = arg ;
struct cc_core_instance * core_instance = obj ;
ast_cli ( * cli_fd , " %d \t \t %s \t \t %s \n " , core_instance - > core_id , core_instance - > agent - > device_name ,
cc_state_to_string ( core_instance - > current_state ) ) ;
AST_LIST_LOCK ( core_instance - > monitors ) ;
cc_cli_print_monitor_stats ( AST_LIST_FIRST ( core_instance - > monitors ) , * cli_fd , 0 ) ;
AST_LIST_UNLOCK ( core_instance - > monitors ) ;
return 0 ;
}
static int cc_cli_output_status ( void * data )
{
int * cli_fd = data ;
int count = ao2_container_count ( cc_core_instances ) ;
if ( ! count ) {
ast_cli ( * cli_fd , " There are currently no active call completion transactions \n " ) ;
} else {
ast_cli ( * cli_fd , " %d Call completion transactions \n " , count ) ;
ast_cli ( * cli_fd , " Core ID \t \t Caller \t \t \t \t Status \n " ) ;
ast_cli ( * cli_fd , " ---------------------------------------------------------------------------- \n " ) ;
ao2_t_callback ( cc_core_instances , OBJ_NODATA , print_stats_cb , cli_fd , " Printing stats to CLI " ) ;
}
ast_free ( cli_fd ) ;
return 0 ;
}
static char * handle_cc_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
int * cli_fd ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " cc report status " ;
e - > usage =
" Usage: cc report status \n "
" Report the current status of any ongoing CC transactions \n " ;
return NULL ;
case CLI_GENERATE :
return NULL ;
}
if ( a - > argc ! = 3 ) {
return CLI_SHOWUSAGE ;
}
cli_fd = ast_malloc ( sizeof ( * cli_fd ) ) ;
if ( ! cli_fd ) {
return CLI_FAILURE ;
}
* cli_fd = a - > fd ;
if ( ast_taskprocessor_push ( cc_core_taskprocessor , cc_cli_output_status , cli_fd ) ) {
ast_free ( cli_fd ) ;
return CLI_FAILURE ;
}
return CLI_SUCCESS ;
}
static int kill_cores ( void * obj , void * arg , int flags )
{
int * core_id = arg ;
struct cc_core_instance * core_instance = obj ;
if ( ! core_id | | ( core_instance - > core_id = = * core_id ) ) {
ast_cc_failed ( core_instance - > core_id , " CC transaction canceled administratively \n " ) ;
}
return 0 ;
}
static char * complete_core_id ( const char * line , const char * word , int pos , int state )
{
int which = 0 ;
int wordlen = strlen ( word ) ;
char * ret = NULL ;
struct ao2_iterator core_iter = ao2_iterator_init ( cc_core_instances , 0 ) ;
struct cc_core_instance * core_instance ;
for ( ; ( core_instance = ao2_t_iterator_next ( & core_iter , " Next core instance " ) ) ;
cc_unref ( core_instance , " CLI tab completion iteration " ) ) {
char core_id_str [ 20 ] ;
snprintf ( core_id_str , sizeof ( core_id_str ) , " %d " , core_instance - > core_id ) ;
if ( ! strncmp ( word , core_id_str , wordlen ) & & + + which > state ) {
ret = ast_strdup ( core_id_str ) ;
cc_unref ( core_instance , " Found a matching core ID for CLI tab-completion " ) ;
break ;
}
}
ao2_iterator_destroy ( & core_iter ) ;
return ret ;
}
static char * handle_cc_kill ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
static const char * const option [ ] = { " core " , " all " , NULL } ;
switch ( cmd ) {
case CLI_INIT :
e - > command = " cc cancel " ;
e - > usage =
" Usage: cc cancel can be used in two ways. \n "
" 1. 'cc cancel core [core ID]' will cancel the CC transaction with \n "
" core ID equal to the specified core ID. \n "
" 2. 'cc cancel all' will cancel all active CC transactions. \n " ;
return NULL ;
case CLI_GENERATE :
if ( a - > pos = = 2 ) {
return ast_cli_complete ( a - > word , option , a - > n ) ;
}
if ( a - > pos = = 3 ) {
return complete_core_id ( a - > line , a - > word , a - > pos , a - > n ) ;
}
return NULL ;
}
if ( a - > argc = = 4 ) {
int core_id ;
char * endptr ;
if ( strcasecmp ( a - > argv [ 2 ] , " core " ) ) {
return CLI_SHOWUSAGE ;
}
core_id = strtol ( a - > argv [ 3 ] , & endptr , 10 ) ;
if ( ( errno ! = 0 & & core_id = = 0 ) | | ( endptr = = a - > argv [ 3 ] ) ) {
return CLI_SHOWUSAGE ;
}
ao2_t_callback ( cc_core_instances , OBJ_NODATA , kill_cores , & core_id , " CLI Killing Core Id " ) ;
} else if ( a - > argc = = 3 ) {
if ( strcasecmp ( a - > argv [ 2 ] , " all " ) ) {
return CLI_SHOWUSAGE ;
}
ao2_t_callback ( cc_core_instances , OBJ_NODATA , kill_cores , NULL , " CLI Killing all CC cores " ) ;
} else {
return CLI_SHOWUSAGE ;
}
return CLI_SUCCESS ;
}
static struct ast_cli_entry cc_cli [ ] = {
AST_CLI_DEFINE ( handle_cc_status , " Reports CC stats " ) ,
AST_CLI_DEFINE ( handle_cc_kill , " Kill a CC transaction " ) ,
} ;
int ast_cc_init ( void )
{
int res ;
if ( ! ( cc_core_instances = ao2_t_container_alloc ( CC_CORE_INSTANCES_BUCKETS ,
cc_core_instance_hash_fn , cc_core_instance_cmp_fn ,
" Create core instance container " ) ) ) {
return - 1 ;
}
if ( ! ( generic_monitors = ao2_t_container_alloc ( CC_CORE_INSTANCES_BUCKETS ,
generic_monitor_hash_fn , generic_monitor_cmp_fn ,
" Create generic monitor container " ) ) ) {
return - 1 ;
}
if ( ! ( cc_core_taskprocessor = ast_taskprocessor_get ( " CCSS core " , TPS_REF_DEFAULT ) ) ) {
return - 1 ;
}
if ( ! ( cc_sched_thread = ast_sched_thread_create ( ) ) ) {
return - 1 ;
}
res = ast_register_application2 ( ccreq_app , ccreq_exec , NULL , NULL , NULL ) ;
res | = ast_register_application2 ( cccancel_app , cccancel_exec , NULL , NULL , NULL ) ;
res | = ast_cc_monitor_register ( & generic_monitor_cbs ) ;
res | = ast_cc_agent_register ( & generic_agent_callbacks ) ;
ast_cli_register_multiple ( cc_cli , ARRAY_LEN ( cc_cli ) ) ;
cc_logger_level = ast_logger_register_level ( CC_LOGGER_LEVEL_NAME ) ;
dialed_cc_interface_counter = 1 ;
initialize_cc_max_requests ( ) ;
return res ;
}