@ -136,13 +136,13 @@ struct stasis_message_type *cel_generic_type(void);
STASIS_MESSAGE_TYPE_DEFN ( cel_generic_type ) ;
/*! Container for CEL backend information */
static struct ao2_container * cel_backends ;
static AO2_GLOBAL_OBJ_STATIC ( cel_backends ) ;
/*! The number of buckets into which backend names will be hashed */
# define BACKEND_BUCKETS 13
/*! Container for dial end multichannel blobs for holding on to dial statuses */
static struct ao2_container * cel_dialstatus_store ;
static AO2_GLOBAL_OBJ_STATIC ( cel_dialstatus_store ) ;
/*!
* \ brief Maximum possible CEL event IDs
@ -160,14 +160,15 @@ static struct ao2_container *cel_dialstatus_store;
*/
# define NUM_DIALSTATUS_BUCKETS 251
/*!
* \ brief Container of Asterisk application names
*
* The apps in this container are the applications that were specified
* in the configuration as applications that CEL events should be generated
* for when they start and end on a channel .
*/
static struct ao2_container * linkedids ;
struct cel_linkedid {
/*! Number of channels with this linkedid. */
unsigned int count ;
/*! Linkedid stored at end of struct. */
char id [ 0 ] ;
} ;
/*! Container of channel references to a linkedid for CEL purposes. */
static AO2_GLOBAL_OBJ_STATIC ( cel_linkedids ) ;
/*! \brief Destructor for cel_config */
static void cel_general_config_dtor ( void * obj )
@ -314,12 +315,12 @@ static int cel_backend_hash(const void *obj, int flags)
const struct cel_backend * backend ;
const char * name ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
case OBJ_ POINTER :
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_ SEARCH_OBJECT :
backend = obj ;
name = backend - > name ;
break ;
case OBJ_ KEY:
case OBJ_ SEARCH_ KEY:
name = obj ;
break ;
default :
@ -334,24 +335,41 @@ static int cel_backend_hash(const void *obj, int flags)
/*! \brief Comparator function for cel_backend */
static int cel_backend_cmp ( void * obj , void * arg , int flags )
{
struct cel_backend * backend2 , * backend1 = obj ;
const char * backend2_id , * backend1_id = backend1 - > name ;
switch ( flags & ( OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY ) ) {
case OBJ_POINTER :
backend2 = arg ;
backend2_id = backend2 - > name ;
const struct cel_backend * object_left = obj ;
const struct cel_backend * object_right = arg ;
const char * right_key = arg ;
int cmp ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_OBJECT :
right_key = object_right - > name ;
/* Fall through */
case OBJ_SEARCH_KEY :
cmp = strcmp ( object_left - > name , right_key ) ;
break ;
case OBJ_KEY :
backend2_id = arg ;
case OBJ_SEARCH_PARTIAL_KEY :
/*
* We could also use a partial key struct containing a length
* so strlen ( ) does not get called for every comparison instead .
*/
cmp = strncmp ( object_left - > name , right_key , strlen ( right_key ) ) ;
break ;
default :
/* Hash can only work on something with a full key. */
ast_assert ( 0 ) ;
/*
* What arg points to is specific to this traversal callback
* and has no special meaning to astobj2 .
*/
cmp = 0 ;
break ;
}
if ( cmp ) {
return 0 ;
}
return ! strcmp ( backend1_id , backend2_id ) ? CMP_MATCH | CMP_STOP : 0 ;
/*
* At this point the traversal callback is identical to a sorted
* container .
*/
return CMP_MATCH ;
}
static const char * get_caller_uniqueid ( struct ast_multi_channel_blob * blob )
@ -367,60 +385,82 @@ static const char *get_caller_uniqueid(struct ast_multi_channel_blob *blob)
/*! \brief Hashing function for dialstatus container */
static int dialstatus_hash ( const void * obj , int flags )
{
struct ast_multi_channel_blob * blob = ( void * ) obj ;
const char * uniqueid = obj ;
if ( ! ( flags & OBJ_KEY ) ) {
uniqueid = get_caller_uniqueid ( blob ) ;
}
struct ast_multi_channel_blob * blob ;
const char * key ;
return ast_str_hash ( uniqueid ) ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_KEY :
key = obj ;
break ;
case OBJ_SEARCH_OBJECT :
blob = ( void * ) obj ;
key = get_caller_uniqueid ( blob ) ;
break ;
default :
/* Hash can only work on something with a full key. */
ast_assert ( 0 ) ;
return 0 ;
}
return ast_str_hash ( key ) ;
}
/*! \brief Comparator function for dialstatus container */
static int dialstatus_cmp ( void * obj , void * arg , int flags )
{
struct ast_multi_channel_blob * blob1 = obj , * blob2 = arg ;
const char * blob2_id = arg , * blob1_id = get_caller_uniqueid ( blob1 ) ;
if ( ! ( flags & OBJ_KEY ) ) {
blob2_id = get_caller_uniqueid ( blob2 ) ;
struct ast_multi_channel_blob * object_left = obj ;
struct ast_multi_channel_blob * object_right = arg ;
const char * right_key = arg ;
int cmp ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_OBJECT :
right_key = get_caller_uniqueid ( object_right ) ;
/* Fall through */
case OBJ_SEARCH_KEY :
cmp = strcmp ( get_caller_uniqueid ( object_left ) , right_key ) ;
break ;
case OBJ_SEARCH_PARTIAL_KEY :
/*
* We could also use a partial key struct containing a length
* so strlen ( ) does not get called for every comparison instead .
*/
cmp = strncmp ( get_caller_uniqueid ( object_left ) , right_key , strlen ( right_key ) ) ;
break ;
default :
/*
* What arg points to is specific to this traversal callback
* and has no special meaning to astobj2 .
*/
cmp = 0 ;
break ;
}
return ! strcmp ( blob1_id , blob2_id ) ? CMP_MATCH | CMP_STOP : 0 ;
}
unsigned int ast_cel_check_enabled ( void )
{
RAII_VAR ( struct cel_config * , cfg , ao2_global_obj_ref ( cel_configs ) , ao2_cleanup ) ;
if ( ! cfg | | ! cfg - > general ) {
if ( cmp ) {
return 0 ;
}
return cfg - > general - > enable ;
}
static int print_app ( void * obj , void * arg , int flags )
{
struct ast_cli_args * a = arg ;
ast_cli ( a - > fd , " CEL Tracking Application: %s \n " , ( const char * ) obj ) ;
return 0 ;
/*
* At this point the traversal callback is identical to a sorted
* container .
*/
return CMP_MATCH ;
}
static int event_desc_cb ( void * obj , void * arg , int flags )
unsigned int ast_cel_check_enabled ( void )
{
struct ast_cli_args * a = arg ;
struct cel_ backend * backend = obj ;
unsigned int enabled ;
struct cel_config * cfg = ao2_global_obj_ref ( cel_configs ) ;
ast_cli ( a - > fd , " CEL Event Subscriber: %s \n " , backend - > name ) ;
return 0 ;
enabled = ( ! cfg | | ! cfg - > general ) ? 0 : cfg - > general - > enable ;
ao2_cleanup ( cfg ) ;
return enabled ;
}
static char * handle_cli_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
unsigned int i ;
RAII_VAR ( struct cel_config * , cfg , ao2_global_obj_ref ( cel_configs ) , ao2_cleanup ) ;
RAII_VAR ( struct ao2_container * , backends , ao2_global_obj_ref ( cel_backends ) , ao2_cleanup ) ;
struct ao2_iterator iter ;
char * app ;
switch ( cmd ) {
case CLI_INIT :
@ -441,11 +481,7 @@ static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli ( a - > fd , " CEL Logging: %s \n " , ast_cel_check_enabled ( ) ? " Enabled " : " Disabled " ) ;
if ( ! cfg | | ! cfg - > general ) {
return CLI_SUCCESS ;
}
if ( ! cfg - > general - > enable ) {
if ( ! cfg | | ! cfg - > general | | ! cfg - > general - > enable ) {
return CLI_SUCCESS ;
}
@ -462,8 +498,21 @@ static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_
}
}
ao2_callback ( cfg - > general - > apps , OBJ_NODATA , print_app , a ) ;
ao2_callback ( cel_backends , OBJ_MULTIPLE | OBJ_NODATA , event_desc_cb , a ) ;
iter = ao2_iterator_init ( cfg - > general - > apps , 0 ) ;
for ( ; ( app = ao2_iterator_next ( & iter ) ) ; ao2_ref ( app , - 1 ) ) {
ast_cli ( a - > fd , " CEL Tracking Application: %s \n " , app ) ;
}
ao2_iterator_destroy ( & iter ) ;
if ( backends ) {
struct cel_backend * backend ;
iter = ao2_iterator_init ( backends , 0 ) ;
for ( ; ( backend = ao2_iterator_next ( & iter ) ) ; ao2_ref ( backend , - 1 ) ) {
ast_cli ( a - > fd , " CEL Event Subscriber: %s \n " , backend - > name ) ;
}
ao2_iterator_destroy ( & iter ) ;
}
return CLI_SUCCESS ;
}
@ -563,7 +612,7 @@ static int cel_track_app(const char *const_app)
}
app_lower = ast_str_to_lower ( ast_strdupa ( const_app ) ) ;
app = ao2_find ( cfg - > general - > apps , app_lower , OBJ_ KEY) ;
app = ao2_find ( cfg - > general - > apps , app_lower , OBJ_ SEARCH_ KEY) ;
if ( ! app ) {
return 0 ;
}
@ -572,6 +621,7 @@ static int cel_track_app(const char *const_app)
}
static int cel_linkedid_ref ( const char * linkedid ) ;
struct ast_event * ast_cel_create_event ( struct ast_channel_snapshot * snapshot ,
enum ast_cel_event_type event_type , const char * userdefevname ,
struct ast_json * extra , const char * peer )
@ -620,21 +670,18 @@ static int cel_report_event(struct ast_channel_snapshot *snapshot,
struct ast_json * extra , const char * peer_str )
{
struct ast_event * ev ;
char * linkedid = ast_strdupa ( snapshot - > linkedid ) ;
RAII_VAR ( struct cel_config * , cfg , ao2_global_obj_ref ( cel_configs ) , ao2_cleanup ) ;
RAII_VAR ( struct ao2_container * , backends , ao2_global_obj_ref ( cel_backends ) , ao2_cleanup ) ;
if ( ! cfg | | ! cfg - > general ) {
return 0 ;
}
if ( ! cfg - > general - > enable ) {
if ( ! cfg | | ! cfg - > general | | ! cfg - > general - > enable | | ! backends ) {
return 0 ;
}
/* Record the linkedid of new channels if we are tracking LINKEDID_END even if we aren't
* reporting on CHANNEL_START so we can track when to send LINKEDID_END */
if ( ast_cel_track_event ( AST_CEL_LINKEDID_END ) & & event_type = = AST_CEL_CHANNEL_START & & linkedid ) {
if ( cel_linkedid_ref ( linkedid ) ) {
if ( event_type = = AST_CEL_CHANNEL_START
& & ast_cel_track_event ( AST_CEL_LINKEDID_END ) ) {
if ( cel_linkedid_ref ( snapshot - > linkedid ) ) {
return - 1 ;
}
}
@ -654,7 +701,7 @@ static int cel_report_event(struct ast_channel_snapshot *snapshot,
}
/* Distribute event to backends */
ao2_callback ( cel_ backends, OBJ_MULTIPLE | OBJ_NODATA , cel_backend_send_cb , ev ) ;
ao2_callback ( backends, OBJ_MULTIPLE | OBJ_NODATA , cel_backend_send_cb , ev ) ;
ast_event_destroy ( ev ) ;
return 0 ;
@ -664,24 +711,38 @@ static int cel_report_event(struct ast_channel_snapshot *snapshot,
* potentially emit a CEL_LINKEDID_END event */
static void check_retire_linkedid ( struct ast_channel_snapshot * snapshot )
{
char * lid ;
/* make sure we need to do all this work */
RAII_VAR ( struct ao2_container * , linkedids , ao2_global_obj_ref ( cel_linkedids ) , ao2_cleanup ) ;
struct cel_linkedid * lid ;
if ( ast_strlen_zero ( snapshot - > linkedid ) | | ! ast_cel_track_event ( AST_CEL_LINKEDID_END ) ) {
if ( ! linkedids | | ast_strlen_zero ( snapshot - > linkedid ) ) {
/* The CEL module is shutdown. Abort. */
return ;
}
if ( ! ( lid = ao2_find ( linkedids , ( void * ) snapshot - > linkedid , OBJ_POINTER ) ) ) {
ast_log ( LOG_ERROR , " Something weird happened, couldn't find linkedid %s \n " , snapshot - > linkedid ) ;
ao2_lock ( linkedids ) ;
lid = ao2_find ( linkedids , ( void * ) snapshot - > linkedid , OBJ_SEARCH_KEY ) ;
if ( ! lid ) {
ao2_unlock ( linkedids ) ;
/*
* The user may have done a reload to start tracking linkedids
* when a call was already in progress . This is an unusual kind
* of change to make after starting Asterisk .
*/
ast_log ( LOG_ERROR , " Something weird happened, couldn't find linkedid %s \n " ,
snapshot - > linkedid ) ;
return ;
}
/* We have a ref for each channel with this linkedid, the link and the above find, so if
* before unreffing the channel we have a refcount of 3 , we ' re done . Unlink and report . */
if ( ao2_ref ( lid , - 1 ) = = 3 ) {
ast_str_container_remove ( linkedids , lid ) ;
if ( ! - - lid - > count ) {
/* No channels use this linkedid anymore. */
ao2_unlink ( linkedids , lid ) ;
ao2_unlock ( linkedids ) ;
cel_report_event ( snapshot , AST_CEL_LINKEDID_END , NULL , NULL , NULL ) ;
} else {
ao2_unlock ( linkedids ) ;
}
ao2_ref ( lid , - 1 ) ;
}
@ -818,26 +879,39 @@ struct ast_channel *ast_cel_fabricate_channel_from_event(const struct ast_event
static int cel_linkedid_ref ( const char * linkedid )
{
char * lid ;
RAII_VAR ( struct ao2_container * , linkedids , ao2_global_obj_ref ( cel_linkedids ) , ao2_cleanup ) ;
struct cel_linkedid * lid ;
if ( ast_strlen_zero ( linkedid ) ) {
ast_log ( LOG_ERROR , " The linkedid should never be empty \n " ) ;
return - 1 ;
}
if ( ! linkedids ) {
/* The CEL module is shutdown. Abort. */
return - 1 ;
}
if ( ! ( lid = ao2_find ( linkedids , ( void * ) linkedid , OBJ_POINTER ) ) ) {
if ( ! ( lid = ao2_alloc ( strlen ( linkedid ) + 1 , NULL ) ) ) {
return - 1 ;
}
strcpy ( lid , linkedid ) ;
if ( ! ao2_link ( linkedids , lid ) ) {
ao2_ref ( lid , - 1 ) ;
ao2_lock ( linkedids ) ;
lid = ao2_find ( linkedids , ( void * ) linkedid , OBJ_SEARCH_KEY ) ;
if ( ! lid ) {
/*
* Changes to the lid - > count member are protected by the
* container lock so the lid object does not need its own lock .
*/
lid = ao2_alloc_options ( sizeof ( * lid ) + strlen ( linkedid ) + 1 , NULL ,
AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( ! lid ) {
ao2_unlock ( linkedids ) ;
return - 1 ;
}
/* Leave both the link and the alloc refs to show a count of 1 + the link */
strcpy ( lid - > id , linkedid ) ; /* Safe */
ao2_link ( linkedids , lid ) ;
}
/* If we've found, go ahead and keep the ref to increment count of how many channels
* have this linkedid . We ' ll clean it up in check_retire */
+ + lid - > count ;
ao2_unlock ( linkedids ) ;
ao2_ref ( lid , - 1 ) ;
return 0 ;
}
@ -891,7 +965,14 @@ typedef void (*cel_channel_snapshot_monitor)(
static struct ast_multi_channel_blob * get_dialstatus_blob ( const char * uniqueid )
{
return ao2_find ( cel_dialstatus_store , uniqueid , OBJ_KEY | OBJ_UNLINK ) ;
struct ao2_container * dial_statuses = ao2_global_obj_ref ( cel_dialstatus_store ) ;
struct ast_multi_channel_blob * blob = NULL ;
if ( dial_statuses ) {
blob = ao2_find ( dial_statuses , uniqueid , OBJ_SEARCH_KEY | OBJ_UNLINK ) ;
ao2_ref ( dial_statuses , - 1 ) ;
}
return blob ;
}
static const char * get_blob_variable ( struct ast_multi_channel_blob * blob , const char * varname )
@ -918,7 +999,9 @@ static void cel_channel_state_change(
if ( ! new_snapshot ) {
cel_report_event ( old_snapshot , AST_CEL_CHANNEL_END , NULL , NULL , NULL ) ;
check_retire_linkedid ( old_snapshot ) ;
if ( ast_cel_track_event ( AST_CEL_LINKEDID_END ) ) {
check_retire_linkedid ( old_snapshot ) ;
}
return ;
}
@ -931,9 +1014,10 @@ static void cel_channel_state_change(
is_hungup = ast_test_flag ( & new_snapshot - > flags , AST_FLAG_DEAD ) ? 1 : 0 ;
if ( ! was_hungup & & is_hungup ) {
RAII_VAR ( struct ast_json * , extra , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_multi_channel_blob * , blob , get_dialstatus_blob ( new_snapshot - > uniqueid ) , ao2_cleanup ) ;
struct ast_json * extra ;
struct ast_multi_channel_blob * blob = get_dialstatus_blob ( new_snapshot - > uniqueid ) ;
const char * dialstatus = " " ;
if ( blob & & ! ast_strlen_zero ( get_blob_variable ( blob , " dialstatus " ) ) ) {
dialstatus = get_blob_variable ( blob , " dialstatus " ) ;
}
@ -942,6 +1026,8 @@ static void cel_channel_state_change(
" hangupsource " , new_snapshot - > hangupsource ,
" dialstatus " , dialstatus ) ;
cel_report_event ( new_snapshot , AST_CEL_HANGUP , NULL , extra , NULL ) ;
ast_json_unref ( extra ) ;
ao2_cleanup ( blob ) ;
return ;
}
@ -962,7 +1048,8 @@ static void cel_channel_linkedid_change(
ast_assert ( ! ast_strlen_zero ( new_snapshot - > linkedid ) ) ;
ast_assert ( ! ast_strlen_zero ( old_snapshot - > linkedid ) ) ;
if ( strcmp ( old_snapshot - > linkedid , new_snapshot - > linkedid ) ) {
if ( ast_cel_track_event ( AST_CEL_LINKEDID_END )
& & strcmp ( old_snapshot - > linkedid , new_snapshot - > linkedid ) ) {
cel_linkedid_ref ( new_snapshot - > linkedid ) ;
check_retire_linkedid ( old_snapshot ) ;
}
@ -1044,9 +1131,7 @@ static struct ast_str *cel_generate_peer_str(
for ( i = ao2_iterator_init ( bridge - > channels , 0 ) ;
( current_chan = ao2_iterator_next ( & i ) ) ;
ao2_cleanup ( current_chan ) ) {
RAII_VAR ( struct ast_channel_snapshot * , current_snapshot ,
NULL ,
ao2_cleanup ) ;
struct ast_channel_snapshot * current_snapshot ;
/* Don't add the channel for which this message is being generated */
if ( ! strcmp ( current_chan , chan - > uniqueid ) ) {
@ -1059,6 +1144,7 @@ static struct ast_str *cel_generate_peer_str(
}
ast_str_append ( & peer_str , 0 , " %s, " , current_snapshot - > name ) ;
ao2_cleanup ( current_snapshot ) ;
}
ao2_iterator_destroy ( & i ) ;
@ -1164,7 +1250,14 @@ static void cel_parking_cb(
static void save_dialstatus ( struct ast_multi_channel_blob * blob )
{
ao2_link ( cel_dialstatus_store , blob ) ;
struct ao2_container * dial_statuses = ao2_global_obj_ref ( cel_dialstatus_store ) ;
ast_assert ( blob ! = NULL ) ;
if ( dial_statuses ) {
ao2_link ( dial_statuses , blob ) ;
ao2_ref ( dial_statuses , - 1 ) ;
}
}
static void cel_dial_cb ( void * data , struct stasis_subscription * sub ,
@ -1182,7 +1275,8 @@ static void cel_dial_cb(void *data, struct stasis_subscription *sub,
if ( ! ast_strlen_zero ( get_blob_variable ( blob , " forward " ) ) ) {
struct ast_channel_snapshot * caller = ast_multi_channel_blob_get_channel ( blob , " caller " ) ;
RAII_VAR ( struct ast_json * , extra , NULL , ast_json_unref ) ;
struct ast_json * extra ;
if ( ! caller ) {
return ;
}
@ -1190,6 +1284,7 @@ static void cel_dial_cb(void *data, struct stasis_subscription *sub,
extra = ast_json_pack ( " {s: s} " , " forward " , get_blob_variable ( blob , " forward " ) ) ;
if ( extra ) {
cel_report_event ( caller , AST_CEL_FORWARD , NULL , extra , NULL ) ;
ast_json_unref ( extra ) ;
}
}
@ -1233,8 +1328,9 @@ static void cel_blind_transfer_cb(
struct ast_json * json_result = ast_json_object_get ( blob , " result " ) ;
struct ast_json * json_exten ;
struct ast_json * json_context ;
RAII_VAR ( struct ast_json * , extra , NULL , ast_json_unref ) ;
const char * exten , * context ;
struct ast_json * extra ;
const char * exten ;
const char * context ;
enum ast_transfer_result result ;
if ( ! json_result ) {
@ -1263,9 +1359,9 @@ static void cel_blind_transfer_cb(
" extension " , exten ,
" context " , context ,
" bridge_id " , bridge_snapshot - > uniqueid ) ;
if ( extra ) {
cel_report_event ( chan_snapshot , AST_CEL_BLINDTRANSFER , NULL , extra , NULL ) ;
ast_json_unref ( extra ) ;
}
}
@ -1274,7 +1370,7 @@ static void cel_attended_transfer_cb(
struct stasis_message * message )
{
struct ast_attended_transfer_message * xfer = stasis_message_data ( message ) ;
RAII_VAR ( struct ast_json * , extra , NULL , ast_json_unref ) ;
struct ast_json * extra = NULL ;
struct ast_bridge_snapshot * bridge1 , * bridge2 ;
struct ast_channel_snapshot * channel1 , * channel2 ;
@ -1302,7 +1398,6 @@ static void cel_attended_transfer_cb(
" bridge1_id " , bridge1 - > uniqueid ,
" channel2_name " , channel2 - > name ,
" bridge2_id " , bridge2 - > uniqueid ) ;
if ( ! extra ) {
return ;
}
@ -1312,13 +1407,13 @@ static void cel_attended_transfer_cb(
" bridge1_id " , bridge1 - > uniqueid ,
" channel2_name " , channel2 - > name ,
" app " , xfer - > dest . app ) ;
if ( ! extra ) {
return ;
}
break ;
}
cel_report_event ( channel1 , AST_CEL_ATTENDEDTRANSFER , NULL , extra , NULL ) ;
ast_json_unref ( extra ) ;
}
static void cel_pickup_cb (
@ -1328,7 +1423,7 @@ static void cel_pickup_cb(
struct ast_multi_channel_blob * obj = stasis_message_data ( message ) ;
struct ast_channel_snapshot * channel = ast_multi_channel_blob_get_channel ( obj , " channel " ) ;
struct ast_channel_snapshot * target = ast_multi_channel_blob_get_channel ( obj , " target " ) ;
RAII_VAR ( struct ast_json * , extra , NULL , ast_json_unref ) ;
struct ast_json * extra ;
if ( ! channel | | ! target ) {
return ;
@ -1340,6 +1435,7 @@ static void cel_pickup_cb(
}
cel_report_event ( target , AST_CEL_PICKUP , NULL , extra , NULL ) ;
ast_json_unref ( extra ) ;
}
static void cel_local_cb (
@ -1349,7 +1445,7 @@ static void cel_local_cb(
struct ast_multi_channel_blob * obj = stasis_message_data ( message ) ;
struct ast_channel_snapshot * localone = ast_multi_channel_blob_get_channel ( obj , " 1 " ) ;
struct ast_channel_snapshot * localtwo = ast_multi_channel_blob_get_channel ( obj , " 2 " ) ;
RAII_VAR ( struct ast_json * , extra , NULL , ast_json_unref ) ;
struct ast_json * extra ;
if ( ! localone | | ! localtwo ) {
return ;
@ -1361,6 +1457,7 @@ static void cel_local_cb(
}
cel_report_event ( localone , AST_CEL_LOCAL_OPTIMIZE , NULL , extra , NULL ) ;
ast_json_unref ( extra ) ;
}
static void destroy_routes ( void )
@ -1382,21 +1479,27 @@ static void destroy_subscriptions(void)
cel_cel_forwarder = stasis_forward_cancel ( cel_cel_forwarder ) ;
}
static void ast_cel_engine_term ( void )
static void cel_engine_cleanup ( void )
{
destroy_routes ( ) ;
destroy_subscriptions ( ) ;
STASIS_MESSAGE_TYPE_CLEANUP ( cel_generic_type ) ;
}
static void cel_engine_atexit ( void )
{
ast_cli_unregister ( & cli_status ) ;
aco_info_destroy ( & cel_cfg_info ) ;
ao2_global_obj_release ( cel_configs ) ;
ast_cli_unregister ( & cli_status ) ;
ao2_cleanup ( cel_dialstatus_store ) ;
cel_dialstatus_store = NULL ;
ao2_cleanup ( linkedids ) ;
linkedids = NULL ;
ao2_cleanup ( cel_backends ) ;
cel_backends = NULL ;
STASIS_MESSAGE_TYPE_CLEANUP ( cel_generic_type ) ;
ao2_global_obj_release ( cel_dialstatus_store ) ;
ao2_global_obj_release ( cel_linkedids ) ;
ao2_global_obj_release ( cel_backends ) ;
}
static void cel_engine_abort ( void )
{
cel_engine_cleanup ( ) ;
cel_engine_atexit ( ) ;
}
/*!
@ -1514,30 +1617,107 @@ static int create_routes(void)
return ret ;
}
static int lid_hash ( const void * obj , const int flags )
{
const struct cel_linkedid * lid ;
const char * key ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_KEY :
key = obj ;
break ;
case OBJ_SEARCH_OBJECT :
lid = obj ;
key = lid - > id ;
break ;
default :
/* Hash can only work on something with a full key. */
ast_assert ( 0 ) ;
return 0 ;
}
return ast_str_hash ( key ) ;
}
static int lid_cmp ( void * obj , void * arg , int flags )
{
const struct cel_linkedid * object_left = obj ;
const struct cel_linkedid * object_right = arg ;
const char * right_key = arg ;
int cmp ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_OBJECT :
right_key = object_right - > id ;
/* Fall through */
case OBJ_SEARCH_KEY :
cmp = strcmp ( object_left - > id , right_key ) ;
break ;
case OBJ_SEARCH_PARTIAL_KEY :
/*
* We could also use a partial key struct containing a length
* so strlen ( ) does not get called for every comparison instead .
*/
cmp = strncmp ( object_left - > id , right_key , strlen ( right_key ) ) ;
break ;
default :
/*
* What arg points to is specific to this traversal callback
* and has no special meaning to astobj2 .
*/
cmp = 0 ;
break ;
}
if ( cmp ) {
return 0 ;
}
/*
* At this point the traversal callback is identical to a sorted
* container .
*/
return CMP_MATCH ;
}
int ast_cel_engine_init ( void )
{
if ( ! ( linkedids = ast_str_container_alloc ( NUM_APP_BUCKETS ) ) ) {
struct ao2_container * container ;
container = ao2_container_alloc ( NUM_APP_BUCKETS , lid_hash , lid_cmp ) ;
ao2_global_obj_replace_unref ( cel_linkedids , container ) ;
ao2_cleanup ( container ) ;
if ( ! container ) {
cel_engine_abort ( ) ;
return - 1 ;
}
if ( ! ( cel_dialstatus_store = ao2_container_alloc ( NUM_DIALSTATUS_BUCKETS , dialstatus_hash , dialstatus_cmp ) ) ) {
container = ao2_container_alloc ( NUM_DIALSTATUS_BUCKETS ,
dialstatus_hash , dialstatus_cmp ) ;
ao2_global_obj_replace_unref ( cel_dialstatus_store , container ) ;
ao2_cleanup ( container ) ;
if ( ! container ) {
cel_engine_abort ( ) ;
return - 1 ;
}
if ( STASIS_MESSAGE_TYPE_INIT ( cel_generic_type ) ) {
cel_engine_abort ( ) ;
return - 1 ;
}
if ( ast_cli_register ( & cli_status ) ) {
cel_engine_abort ( ) ;
return - 1 ;
}
cel_backends = ao2_container_alloc ( BACKEND_BUCKETS , cel_backend_hash , cel_backend_cmp ) ;
if ( ! cel_backends ) {
container = ao2_container_alloc ( BACKEND_BUCKETS , cel_backend_hash , cel_backend_cmp ) ;
ao2_global_obj_replace_unref ( cel_backends , container ) ;
ao2_cleanup ( container ) ;
if ( ! container ) {
cel_engine_abort ( ) ;
return - 1 ;
}
if ( aco_info_init ( & cel_cfg_info ) ) {
cel_engine_abort ( ) ;
return - 1 ;
}
@ -1547,34 +1727,37 @@ int ast_cel_engine_init(void)
aco_option_register_custom ( & cel_cfg_info , " events " , ACO_EXACT , general_options , " " , events_handler , 0 ) ;
if ( aco_process_config ( & cel_cfg_info , 0 ) ) {
RAII_VAR ( struct cel_config * , cel_cfg , cel_config_alloc ( ) , ao2_cleanup ) ;
struct cel_config * cel_cfg = cel_config_alloc ( ) ;
if ( ! cel_cfg ) {
cel_engine_abort ( ) ;
return - 1 ;
}
/* If we couldn't process the configuration and this wasn't a reload,
* create a default config
*/
/* We couldn't process the configuration so create a default config. */
if ( ! aco_set_defaults ( & general_option , " general " , cel_cfg - > general ) ) {
ast_log ( LOG_NOTICE , " Failed to process CEL configuration; using defaults \n " ) ;
ao2_global_obj_replace_unref ( cel_configs , cel_cfg ) ;
}
ao2_ref ( cel_cfg , - 1 ) ;
}
if ( create_subscriptions ( ) ) {
cel_engine_abort ( ) ;
return - 1 ;
}
if ( ast_cel_check_enabled ( ) & & create_routes ( ) ) {
cel_engine_abort ( ) ;
return - 1 ;
}
ast_register_atexit ( & ast_cel_engine_term ) ;
ast_register_atexit ( cel_engine_atexit ) ;
ast_register_cleanup ( cel_engine_cleanup ) ;
return 0 ;
}
static int do _reload( void )
int ast_cel_engine _reload( void )
{
unsigned int was_enabled = ast_cel_check_enabled ( ) ;
unsigned int is_enabled ;
@ -1598,18 +1781,13 @@ static int do_reload(void)
return 0 ;
}
int ast_cel_engine_reload ( void )
{
return do_reload ( ) ;
}
void ast_cel_publish_event ( struct ast_channel * chan ,
enum ast_cel_event_type event_type ,
struct ast_json * blob )
{
RAII_VAR ( struct ast_channel_blob * , obj , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ast_json * , cel_blob , NULL , ast_json_unref ) ;
RAII_VAR ( struct stasis_message * , message , NULL , ao2_cleanup ) ;
struct ast_json * cel_blob ;
struct stasis_message * message ;
cel_blob = ast_json_pack ( " {s: i, s: O} " ,
" event_type " , event_type ,
" event_details " , blob ) ;
@ -1620,6 +1798,8 @@ void ast_cel_publish_event(struct ast_channel *chan,
if ( message ) {
stasis_publish ( ast_cel_topic ( ) , message ) ;
}
ao2_cleanup ( message ) ;
ast_json_unref ( cel_blob ) ;
}
struct stasis_topic * ast_cel_topic ( void )
@ -1641,16 +1821,18 @@ struct ast_cel_general_config *ast_cel_get_config(void)
void ast_cel_set_config ( struct ast_cel_general_config * config )
{
RAII_VAR ( struct cel_config * , mod_cfg , ao2_global_obj_ref ( cel_configs ) , ao2_cleanup ) ;
RAII_VAR ( struct ast_cel_general_config * , cleanup_config , mod_cfg - > general , ao2_cleanup ) ;
int was_enabled = ast_cel_check_enabled ( ) ;
int was_enabled ;
int is_enabled ;
struct ast_cel_general_config * cleanup_config ;
struct cel_config * mod_cfg = ao2_global_obj_ref ( cel_configs ) ;
if ( mod_cfg ) {
was_enabled = ast_cel_check_enabled ( ) ;
cleanup_config = mod_cfg - > general ;
ao2_bump ( config ) ;
mod_cfg - > general = config ;
if ( mod_cfg - > general ) {
ao2_ref ( mod_cfg - > general , + 1 ) ;
}
ao2_cleanup ( cleanup_config ) ;
is_enabled = ast_cel_check_enabled ( ) ;
if ( ! was_enabled & & is_enabled ) {
@ -1658,16 +1840,18 @@ void ast_cel_set_config(struct ast_cel_general_config *config)
} else if ( was_enabled & & ! is_enabled ) {
destroy_routes ( ) ;
}
ao2_ref ( mod_cfg , - 1 ) ;
}
}
int ast_cel_backend_unregister ( const char * name )
{
RAII_VAR ( struct cel_backend * , backend , NULL , ao2_cleanup ) ;
struct ao2_container * backends = ao2_global_obj_ref ( cel_backends ) ;
backend = ao2_find ( cel_backends , name , OBJ_KEY | OBJ_UNLINK ) ;
if ( ! backend ) {
return - 1 ;
if ( backends ) {
ao2_find ( backends , name , OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK ) ;
ao2_ref ( backends , - 1 ) ;
}
return 0 ;
@ -1675,21 +1859,23 @@ int ast_cel_backend_unregister(const char *name)
int ast_cel_backend_register ( const char * name , ast_cel_backend_cb backend_callback )
{
RAII_VAR ( struct cel_backend * , backend , NULL , ao2_cleanup ) ;
RAII_VAR ( struct ao2_container * , backends , ao2_global_obj_ref ( cel_backends ) , ao2_cleanup ) ;
struct cel_backend * backend ;
if ( ast_strlen_zero ( name ) ) {
if ( ! backends | | ast_strlen_zero ( name ) | | ! backend_callback ) {
return - 1 ;
}
backend = ao2_alloc ( sizeof ( * backend ) + 1 + strlen ( name ) , NULL ) ;
/* The backend object is immutable so it doesn't need a lock of its own. */
backend = ao2_alloc_options ( sizeof ( * backend ) + 1 + strlen ( name ) , NULL ,
AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( ! backend ) {
return - 1 ;
}
/* safe strcpy */
strcpy ( backend - > name , name ) ;
strcpy ( backend - > name , name ) ; /* Safe */
backend - > callback = backend_callback ;
ao2_link ( cel_backends , backend ) ;
ao2_link ( backends , backend ) ;
ao2_ref ( backend , - 1 ) ;
return 0 ;
}