@ -21,6 +21,30 @@
* \ brief Stasis application support .
*
* \ author David M . Lee , II < dlee @ digium . com >
*
* < code > res_stasis . so < / code > brings together the various components of the
* Stasis application infrastructure .
*
* First , there ' s the Stasis application handler , stasis_app_exec ( ) . This is
* called by < code > app_stasis . so < / code > to give control of a channel to the
* Stasis application code from the dialplan .
*
* While a channel is in stasis_app_exec ( ) , it has a \ ref stasis_app_control
* object , which may be used to control the channel .
*
* To control the channel , commands may be sent to channel using
* stasis_app_send_command ( ) and stasis_app_send_async_command ( ) .
*
* Alongside this , applications may be registered / unregistered using
* stasis_app_register ( ) / stasis_app_unregister ( ) . While a channel is in Stasis ,
* events received on the channel ' s topic are converted to JSON and forwarded to
* the \ ref stasis_app_cb . The application may also subscribe to the channel to
* continue to receive messages even after the channel has left Stasis , but it
* will not be able to control it .
*
* Given all the stuff that comes together in this module , it ' s been broken up
* into several pieces that are in < code > res / stasis / < / code > and compiled into
* < code > res_stasis . so < / code > .
*/
/*** MODULEINFO
@ -33,15 +57,14 @@
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
# include "asterisk/astobj2.h"
# include "asterisk/channel.h"
# include "asterisk/lock.h"
# include "asterisk/callerid.h"
# include "asterisk/module.h"
# include "asterisk/stasis.h"
# include "asterisk/stasis_app.h"
# include "asterisk/stasis_app_impl.h"
# include "asterisk/stasis_channels.h"
# include "asterisk/strings.h"
# include "asterisk/stasis_message_router.h"
# include "asterisk/callerid.h"
# include "asterisk/strings.h"
# include "stasis/app.h"
# include "stasis/control.h"
# include "stasis_json/resource_events.h"
/*! Time to wait for a frame in the application */
@ -59,12 +82,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
*/
# define CONTROLS_NUM_BUCKETS 127
/*!
* \ brief Number of buckets for the channels container for app instances . Remember
* to keep it a prime number !
*/
# define APP_CHANNELS_BUCKETS 7
/*!
* \ brief Number of buckets for the blob_handlers container . Remember to keep
* it a prime number !
@ -72,85 +89,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# define BLOB_HANDLER_BUCKETS 7
/*!
* \ brief Stasis application container . Please call apps_registry ( ) instead of
* directly accessing .
* \ brief Stasis application container .
*/
struct ao2_container * __ apps_registry;
struct ao2_container * apps_registry;
struct ao2_container * __ app_controls;
struct ao2_container * app_controls;
/*! \brief Message router for the channel caching topic */
struct stasis_message_router * channel_router ;
/*! Ref-counting accessor for the stasis applications container */
static struct ao2_container * apps_registry ( void )
{
ao2_ref ( __apps_registry , + 1 ) ;
return __apps_registry ;
}
static struct ao2_container * app_controls ( void )
{
ao2_ref ( __app_controls , + 1 ) ;
return __app_controls ;
}
struct app {
/*! Callback function for this application. */
stasis_app_cb handler ;
/*! Opaque data to hand to callback function. */
void * data ;
/*! List of channel identifiers this app instance is interested in */
struct ao2_container * channels ;
/*! Name of the Stasis application */
char name [ ] ;
} ;
static void app_dtor ( void * obj )
{
struct app * app = obj ;
ao2_cleanup ( app - > data ) ;
app - > data = NULL ;
ao2_cleanup ( app - > channels ) ;
app - > channels = NULL ;
}
/*! Constructor for \ref app. */
static struct app * app_create ( const char * name , stasis_app_cb handler , void * data )
{
RAII_VAR ( struct app * , app , NULL , ao2_cleanup ) ;
size_t size ;
ast_assert ( name ! = NULL ) ;
ast_assert ( handler ! = NULL ) ;
size = sizeof ( * app ) + strlen ( name ) + 1 ;
app = ao2_alloc_options ( size , app_dtor , AO2_ALLOC_OPT_LOCK_MUTEX ) ;
if ( ! app ) {
return NULL ;
}
strncpy ( app - > name , name , size - sizeof ( * app ) ) ;
app - > handler = handler ;
ao2_ref ( data , + 1 ) ;
app - > data = data ;
app - > channels = ast_str_container_alloc ( APP_CHANNELS_BUCKETS ) ;
if ( ! app - > channels ) {
return NULL ;
}
ao2_ref ( app , + 1 ) ;
return app ;
}
/*! AO2 hash function for \ref app */
static int app_hash ( const void * obj , const int flags )
{
const struct app * app = obj ;
const char * name = flags & OBJ_KEY ? obj : app - > name ;
const char * name = flags & OBJ_KEY ? obj : app_name ( app ) ;
return ast_str_hash ( name ) ;
}
@ -160,149 +112,22 @@ static int app_compare(void *lhs, void *rhs, int flags)
{
const struct app * lhs_app = lhs ;
const struct app * rhs_app = rhs ;
const char * rhs_name = flags & OBJ_KEY ? rhs : rhs_app - > name ;
const char * lhs_name = app_name ( lhs_app ) ;
const char * rhs_name = flags & OBJ_KEY ? rhs : app_name ( rhs_app ) ;
if ( strcmp ( lhs_ app- > name, rhs_name ) = = 0 ) {
if ( strcmp ( lhs_ name, rhs_name ) = = 0 ) {
return CMP_MATCH | CMP_STOP ;
} else {
return 0 ;
}
}
static int app_add_channel ( struct app * app , const struct ast_channel * chan )
{
const char * uniqueid ;
ast_assert ( chan ! = NULL ) ;
ast_assert ( app ! = NULL ) ;
uniqueid = ast_channel_uniqueid ( chan ) ;
if ( ! ast_str_container_add ( app - > channels , uniqueid ) ) {
return - 1 ;
}
return 0 ;
}
static void app_remove_channel ( struct app * app , const struct ast_channel * chan )
{
ast_assert ( chan ! = NULL ) ;
ast_assert ( app ! = NULL ) ;
ao2_find ( app - > channels , ast_channel_uniqueid ( chan ) , OBJ_KEY | OBJ_NODATA | OBJ_UNLINK ) ;
}
/*!
* \ brief Send a message to the given application .
* \ param app App to send the message to .
* \ param message Message to send .
*/
static void app_send ( struct app * app , struct ast_json * message )
{
app - > handler ( app - > data , app - > name , message ) ;
}
typedef void * ( * stasis_app_command_cb ) ( struct stasis_app_control * control ,
struct ast_channel * chan ,
void * data ) ;
struct stasis_app_command {
ast_mutex_t lock ;
ast_cond_t condition ;
stasis_app_command_cb callback ;
void * data ;
void * retval ;
int is_done : 1 ;
} ;
static void command_dtor ( void * obj )
{
struct stasis_app_command * command = obj ;
ast_mutex_destroy ( & command - > lock ) ;
ast_cond_destroy ( & command - > condition ) ;
}
static struct stasis_app_command * command_create ( stasis_app_command_cb callback ,
void * data )
{
RAII_VAR ( struct stasis_app_command * , command , NULL , ao2_cleanup ) ;
command = ao2_alloc ( sizeof ( * command ) , command_dtor ) ;
if ( ! command ) {
return NULL ;
}
ast_mutex_init ( & command - > lock ) ;
ast_cond_init ( & command - > condition , 0 ) ;
command - > callback = callback ;
command - > data = data ;
ao2_ref ( command , + 1 ) ;
return command ;
}
static void command_complete ( struct stasis_app_command * command , void * retval )
{
SCOPED_MUTEX ( lock , & command - > lock ) ;
command - > is_done = 1 ;
command - > retval = retval ;
ast_cond_signal ( & command - > condition ) ;
}
static void * wait_for_command ( struct stasis_app_command * command )
{
SCOPED_MUTEX ( lock , & command - > lock ) ;
while ( ! command - > is_done ) {
ast_cond_wait ( & command - > condition , & command - > lock ) ;
}
return command - > retval ;
}
struct stasis_app_control {
/*! Queue of commands to dispatch on the channel */
struct ao2_container * command_queue ;
/*!
* When set , / c app_stasis should exit and continue in the dialplan .
*/
int continue_to_dialplan : 1 ;
/*! Uniqueid of the associated channel */
char channel_id [ ] ;
} ;
static struct stasis_app_control * control_create ( const char * uniqueid )
{
struct stasis_app_control * control ;
size_t size ;
size = sizeof ( * control ) + strlen ( uniqueid ) + 1 ;
control = ao2_alloc ( size , NULL ) ;
if ( ! control ) {
return NULL ;
}
control - > command_queue = ao2_container_alloc_list ( 0 , 0 , NULL , NULL ) ;
strncpy ( control - > channel_id , uniqueid , size - sizeof ( * control ) ) ;
return control ;
}
static void * exec_command ( struct stasis_app_control * control ,
struct stasis_app_command * command )
{
ao2_lock ( control ) ;
ao2_ref ( command , + 1 ) ;
ao2_link ( control - > command_queue , command ) ;
ao2_unlock ( control ) ;
return wait_for_command ( command ) ;
}
/*! AO2 hash function for \ref stasis_app_control */
static int control_hash ( const void * obj , const int flags )
{
const struct stasis_app_control * control = obj ;
const char * id = flags & OBJ_KEY ? obj : control - > channel_id ;
const char * id = flags & OBJ_KEY ?
obj : stasis_app_control_get_channel_id ( control ) ;
return ast_str_hash ( id ) ;
}
@ -312,10 +137,11 @@ static int control_compare(void *lhs, void *rhs, int flags)
{
const struct stasis_app_control * lhs_control = lhs ;
const struct stasis_app_control * rhs_control = rhs ;
const char * rhs_name =
flags & OBJ_KEY ? rhs : rhs_control - > channel_id ;
const char * lhs_id = stasis_app_control_get_channel_id ( lhs_control ) ;
const char * rhs_id = flags & OBJ_KEY ?
rhs : stasis_app_control_get_channel_id ( rhs_control ) ;
if ( strcmp ( lhs_ control- > channel_id , rhs_name ) = = 0 ) {
if ( strcmp ( lhs_ id, rhs_id ) = = 0 ) {
return CMP_MATCH | CMP_STOP ;
} else {
return 0 ;
@ -336,154 +162,30 @@ struct stasis_app_control *stasis_app_control_find_by_channel(
struct stasis_app_control * stasis_app_control_find_by_channel_id (
const char * channel_id )
{
RAII_VAR ( struct ao2_container * , controls , NULL , ao2_cleanup ) ;
controls = app_controls ( ) ;
return ao2_find ( controls , channel_id , OBJ_KEY ) ;
}
/*!
* \ brief Test the \ c continue_to_dialplan bit for the given \ a app .
*
* The bit is also reset for the next call .
*
* \ param app Application to check the \ c continue_to_dialplan bit .
* \ return Zero to remain in \ c Stasis
* \ return Non - zero to continue in the dialplan
*/
static int control_continue_test_and_reset ( struct stasis_app_control * control )
{
int r ;
SCOPED_AO2LOCK ( lock , control ) ;
r = control - > continue_to_dialplan ;
control - > continue_to_dialplan = 0 ;
return r ;
}
void stasis_app_control_continue ( struct stasis_app_control * control )
{
SCOPED_AO2LOCK ( lock , control ) ;
control - > continue_to_dialplan = 1 ;
return ao2_find ( app_controls , channel_id , OBJ_KEY ) ;
}
/*! \brief Typedef for blob handler callbacks */
typedef struct ast_json * ( * channel_blob_handler_cb ) ( struct ast_channel_blob * ) ;
static int OK = 0 ;
static int FAIL = - 1 ;
static void * __app_control_answer ( struct stasis_app_control * control ,
struct ast_channel * chan , void * data )
{
ast_debug ( 3 , " %s: Answering " , control - > channel_id ) ;
return __ast_answer ( chan , 0 , 1 ) = = 0 ? & OK : & FAIL ;
}
int stasis_app_control_answer ( struct stasis_app_control * control )
{
RAII_VAR ( struct stasis_app_command * , command , NULL , ao2_cleanup ) ;
int * retval ;
ast_debug ( 3 , " %s: Sending answer command \n " , control - > channel_id ) ;
command = command_create ( __app_control_answer , NULL ) ;
retval = exec_command ( control , command ) ;
if ( * retval ! = 0 ) {
ast_log ( LOG_WARNING , " Failed to answer channel " ) ;
}
return * retval ;
}
static int send_start_msg ( struct app * app , struct ast_channel * chan ,
int argc , char * argv [ ] )
{
RAII_VAR ( struct ast_json * , msg , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_json * , blob , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_channel_snapshot * , snapshot , NULL , ao2_cleanup ) ;
struct ast_json * json_args ;
int i ;
ast_assert ( chan ! = NULL ) ;
/* Set channel info */
snapshot = ast_channel_snapshot_create ( chan ) ;
if ( ! snapshot ) {
return - 1 ;
}
blob = ast_json_pack ( " {s: []} " , " args " ) ;
if ( ! blob ) {
return - 1 ;
}
/* Append arguments to args array */
json_args = ast_json_object_get ( blob , " args " ) ;
ast_assert ( json_args ! = NULL ) ;
for ( i = 0 ; i < argc ; + + i ) {
int r = ast_json_array_append ( json_args ,
ast_json_string_create ( argv [ i ] ) ) ;
if ( r ! = 0 ) {
ast_log ( LOG_ERROR , " Error appending start message \n " ) ;
return - 1 ;
}
}
msg = stasis_json_event_stasis_start_create ( snapshot , blob ) ;
if ( ! msg ) {
return - 1 ;
}
app_send ( app , msg ) ;
return 0 ;
}
static int send_end_msg ( struct app * app , struct ast_channel * chan )
{
RAII_VAR ( struct ast_json * , msg , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_channel_snapshot * , snapshot , NULL , ao2_cleanup ) ;
ast_assert ( chan ! = NULL ) ;
/* Set channel info */
snapshot = ast_channel_snapshot_create ( chan ) ;
if ( snapshot = = NULL ) {
return - 1 ;
}
msg = stasis_json_event_stasis_end_create ( snapshot ) ;
if ( ! msg ) {
return - 1 ;
}
app_send ( app , msg ) ;
return 0 ;
}
static int app_watching_channel_cb ( void * obj , void * arg , int flags )
{
RAII_VAR ( char * , uniqueid , NULL , ao2_cleanup ) ;
struct app * app = obj ;
char * chan_ uniqueid = arg ;
char * uniqueid = arg ;
uniqueid = ao2_find ( app - > channels , chan_uniqueid , OBJ_KEY ) ;
return uniqueid ? CMP_MATCH : 0 ;
return app_is_watching_channel ( app , uniqueid ) ? CMP_MATCH : 0 ;
}
static struct ao2_container * get_watching_apps ( const char * uniqueid )
{
RAII_VAR ( struct ao2_container * , apps , apps_registry ( ) , ao2_cleanup ) ;
struct ao2_container * watching_apps ;
char * uniqueid_dup ;
RAII_VAR ( struct ao2_iterator * , watching_apps_iter , NULL , ao2_iterator_destroy ) ;
ast_assert ( uniqueid ! = NULL ) ;
ast_assert ( apps ! = NULL ) ;
uniqueid_dup = ast_strdupa ( uniqueid ) ;
watching_apps_iter = ao2_callback ( apps , OBJ_MULTIPLE , app_watching_channel_cb , uniqueid_dup ) ;
watching_apps_iter = ao2_callback ( apps_registry , OBJ_MULTIPLE , app_watching_channel_cb , uniqueid_dup ) ;
watching_apps = watching_apps_iter - > c ;
if ( ! ao2_container_count ( watching_apps ) ) {
@ -665,72 +367,111 @@ static void generic_blob_handler(struct ast_channel_blob *obj, channel_blob_hand
*/
static void control_unlink ( struct stasis_app_control * control )
{
RAII_VAR ( struct ao2_container * , controls , NULL , ao2_cleanup ) ;
if ( ! control ) {
return ;
}
controls = app_controls ( ) ;
ao2_unlink_flags ( controls , control ,
OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA ) ;
ao2_unlink_flags ( app_controls , control ,
OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA ) ;
ao2_cleanup ( control ) ;
}
static void dispatch_commands ( struct stasis_app_control * control ,
struct ast_channel * chan )
int app_send_start_msg ( struct app * app , struct ast_channel * chan ,
int argc , char * argv [ ] )
{
struct ao2_iterator i ;
void * obj ;
RAII_VAR ( struct ast_json * , msg , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_json * , blob , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_channel_snapshot * , snapshot , NULL , ao2_cleanup ) ;
struct ast_json * json_args ;
int i ;
ast_assert ( chan ! = NULL ) ;
/* Set channel info */
snapshot = ast_channel_snapshot_create ( chan ) ;
if ( ! snapshot ) {
return - 1 ;
}
SCOPED_AO2LOCK ( lock , control ) ;
blob = ast_json_pack ( " {s: []} " , " args " ) ;
if ( ! blob ) {
return - 1 ;
}
i = ao2_iterator_init ( control - > command_queue , AO2_ITERATOR_UNLINK ) ;
/* Append arguments to args array */
json_args = ast_json_object_get ( blob , " args " ) ;
ast_assert ( json_args ! = NULL ) ;
for ( i = 0 ; i < argc ; + + i ) {
int r = ast_json_array_append ( json_args ,
ast_json_string_create ( argv [ i ] ) ) ;
if ( r ! = 0 ) {
ast_log ( LOG_ERROR , " Error appending start message \n " ) ;
return - 1 ;
}
}
while ( ( obj = ao2_iterator_next ( & i ) ) ) {
RAII_VAR ( struct stasis_app_command * , command , obj , ao2_cleanup ) ;
void * retval = command - > callback ( control , chan , command - > data ) ;
command_complete ( command , retval ) ;
msg = stasis_json_event_stasis_start_create ( snapshot , blob ) ;
if ( ! msg ) {
return - 1 ;
}
ao2_iterator_destroy ( & i ) ;
app_send ( app , msg ) ;
return 0 ;
}
int app_send_end_msg ( struct app * app , struct ast_channel * chan )
{
RAII_VAR ( struct ast_json * , msg , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_channel_snapshot * , snapshot , NULL , ao2_cleanup ) ;
ast_assert ( chan ! = NULL ) ;
/* Set channel info */
snapshot = ast_channel_snapshot_create ( chan ) ;
if ( snapshot = = NULL ) {
return - 1 ;
}
msg = stasis_json_event_stasis_end_create ( snapshot ) ;
if ( ! msg ) {
return - 1 ;
}
app_send ( app , msg ) ;
return 0 ;
}
/*! /brief Stasis dialplan application callback */
int stasis_app_exec ( struct ast_channel * chan , const char * app_name , int argc ,
char * argv [ ] )
{
RAII_VAR ( struct ao2_container * , apps , apps_registry ( ) , ao2_cleanup ) ;
SCOPED_MODULE_USE ( ast_module_info - > self ) ;
RAII_VAR ( struct app * , app , NULL , ao2_cleanup ) ;
RAII_VAR ( struct stasis_app_control * , control , NULL , control_unlink ) ;
int res = 0 ;
int hungup = 0 ;
ast_assert ( chan ! = NULL ) ;
app = ao2_find ( apps , app_name , OBJ_KEY ) ;
app = ao2_find ( apps _registry , app_name , OBJ_KEY ) ;
if ( ! app ) {
ast_log ( LOG_ERROR ,
" Stasis app '%s' not registered \n " , app_name ) ;
return - 1 ;
}
{
RAII_VAR ( struct ao2_container * , controls , NULL , ao2_cleanup ) ;
controls = app_controls ( ) ;
control = control_create ( ast_channel_uniqueid ( chan ) ) ;
if ( ! control ) {
ast_log ( LOG_ERROR , " Allocated failed \n " ) ;
return - 1 ;
}
ao2_link ( controls , control ) ;
control = control_create ( chan ) ;
if ( ! control ) {
ast_log ( LOG_ERROR , " Allocated failed \n " ) ;
return - 1 ;
}
ao2_link ( app_controls , control ) ;
res = send_start_msg( app , chan , argc , argv ) ;
res = app_send_start_msg ( app , chan , argc , argv ) ;
if ( res ! = 0 ) {
ast_log ( LOG_ERROR , " Error sending start message to %s \n " , app_name ) ;
ast_log ( LOG_ERROR ,
" Error sending start message to %s \n " , app_name ) ;
return res ;
}
@ -739,21 +480,10 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
return - 1 ;
}
while ( 1 ) {
while ( ! control_is_done ( control ) ) {
RAII_VAR ( struct ast_frame * , f , NULL , ast_frame_dtor ) ;
int r ;
if ( hungup ) {
ast_debug ( 3 , " %s: Hangup \n " ,
ast_channel_uniqueid ( chan ) ) ;
break ;
}
if ( control_continue_test_and_reset ( control ) ) {
ast_debug ( 3 , " %s: Continue \n " ,
ast_channel_uniqueid ( chan ) ) ;
break ;
}
int command_count ;
r = ast_waitfor ( chan , MAX_WAIT_MS ) ;
@ -763,7 +493,12 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
break ;
}
dispatch_commands ( control , chan ) ;
command_count = control_dispatch_all ( control , chan ) ;
if ( command_count > 0 & & ast_channel_fdno ( chan ) = = - 1 ) {
/* Command drained the channel; wait for next frame */
continue ;
}
if ( r = = 0 ) {
/* Timeout */
@ -772,14 +507,19 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
f = ast_read ( chan ) ;
if ( ! f ) {
ast_debug ( 3 , " %s: No more frames. Must be done, I guess. \n " , ast_channel_uniqueid ( chan ) ) ;
ast_debug ( 3 ,
" %s: No more frames. Must be done, I guess. \n " ,
ast_channel_uniqueid ( chan ) ) ;
break ;
}
switch ( f - > frametype ) {
case AST_FRAME_CONTROL :
if ( f - > subclass . integer = = AST_CONTROL_HANGUP ) {
hungup = 1 ;
/* Continue on in the dialplan */
ast_debug ( 3 , " %s: Hangup \n " ,
ast_channel_uniqueid ( chan ) ) ;
control_continue ( control ) ;
}
break ;
default :
@ -789,7 +529,7 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
}
app_remove_channel ( app , chan ) ;
res = send_end_msg( app , chan ) ;
res = app_ send_end_msg( app , chan ) ;
if ( res ! = 0 ) {
ast_log ( LOG_ERROR ,
" Error sending end message to %s \n " , app_name ) ;
@ -801,10 +541,9 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
int stasis_app_send ( const char * app_name , struct ast_json * message )
{
RAII_VAR ( struct ao2_container * , apps , apps_registry ( ) , ao2_cleanup ) ;
RAII_VAR ( struct app * , app , NULL , ao2_cleanup ) ;
app = ao2_find ( apps , app_name , OBJ_KEY ) ;
app = ao2_find ( apps _registry , app_name , OBJ_KEY ) ;
if ( ! app ) {
/* XXX We can do a better job handling late binding, queueing up
@ -821,34 +560,29 @@ int stasis_app_send(const char *app_name, struct ast_json *message)
int stasis_app_register ( const char * app_name , stasis_app_cb handler , void * data )
{
RAII_VAR ( struct ao2_container * , apps , apps_registry ( ) , ao2_cleanup ) ;
RAII_VAR ( struct app * , app , NULL , ao2_cleanup ) ;
SCOPED_LOCK ( apps_lock , apps , ao2_lock , ao2_unlock ) ;
SCOPED_LOCK ( apps_lock , apps _registry , ao2_lock , ao2_unlock ) ;
app = ao2_find ( apps , app_name , OBJ_KEY | OBJ_NOLOCK ) ;
app = ao2_find ( apps _registry , app_name , OBJ_KEY | OBJ_NOLOCK ) ;
if ( app ) {
RAII_VAR ( struct ast_json * , msg , NULL , ast_json_unref ) ;
RAII_VAR ( struct ast_json * , blob , NULL , ast_json_unref ) ;
SCOPED_LOCK( app_lock , app , ao2_lock , ao2_unlock ) ;
RAII_VAR( struct ast_json * , msg , NULL , ast_json_unref ) ;
blob = ast_json_pack ( " {s: s} " , " application " , app_name ) ;
if ( blob ) {
msg = stasis_json_event_application_replaced_create ( blob ) ;
if ( msg ) {
app - > handler ( app - > data , app_name , msg ) ;
app_send ( app , msg ) ;
}
}
app - > handler = handler ;
ao2_cleanup ( app - > data ) ;
ao2_ref ( data , + 1 ) ;
app - > data = data ;
app_update ( app , handler , data ) ;
} else {
app = app_create ( app_name , handler , data ) ;
if ( app ) {
ao2_link_flags ( apps , app , OBJ_NOLOCK ) ;
ao2_link_flags ( apps _registry , app , OBJ_NOLOCK ) ;
} else {
return - 1 ;
}
@ -859,11 +593,9 @@ int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
void stasis_app_unregister ( const char * app_name )
{
RAII_VAR ( struct ao2_container * , apps , NULL , ao2_cleanup ) ;
if ( app_name ) {
apps = apps_registry ( ) ;
ao2_cleanup ( ao2_find ( apps , app_name , OBJ_KEY | OBJ_UNLINK ) ) ;
ao2_cleanup ( ao2_find (
apps_registry , app_name , OBJ_KEY | OBJ_UNLINK ) ) ;
}
}
@ -957,15 +689,15 @@ static int load_module(void)
{
int r = 0 ;
__ apps_registry =
apps_registry =
ao2_container_alloc ( APPS_NUM_BUCKETS , app_hash , app_compare ) ;
if ( __ apps_registry = = NULL ) {
if ( apps_registry = = NULL ) {
return AST_MODULE_LOAD_FAILURE ;
}
__ app_controls = ao2_container_alloc ( CONTROLS_NUM_BUCKETS ,
app_controls = ao2_container_alloc ( CONTROLS_NUM_BUCKETS ,
control_hash , control_compare ) ;
if ( __ app_controls = = NULL ) {
if ( app_controls = = NULL ) {
return AST_MODULE_LOAD_FAILURE ;
}
@ -993,11 +725,11 @@ static int unload_module(void)
stasis_message_router_unsubscribe ( channel_router ) ;
channel_router = NULL ;
ao2_cleanup ( __ apps_registry) ;
__ apps_registry = NULL ;
ao2_cleanup ( apps_registry) ;
apps_registry = NULL ;
ao2_cleanup ( __ app_controls) ;
__ app_controls = NULL ;
ao2_cleanup ( app_controls) ;
app_controls = NULL ;
return r ;
}