@ -72,6 +72,7 @@ ASTERISK_REGISTER_FILE()
# include "asterisk/astobj2.h"
# include "asterisk/astobj2.h"
# include "asterisk/stasis_channels.h"
# include "asterisk/stasis_channels.h"
# include "asterisk/dial.h"
# include "asterisk/dial.h"
# include "asterisk/vector.h"
/*!
/*!
* \ note I M P O R T A N T :
* \ note I M P O R T A N T :
@ -1046,8 +1047,9 @@ struct ast_hint {
char context_name [ AST_MAX_CONTEXT ] ; /*!< Context of destroyed hint extension. */
char context_name [ AST_MAX_CONTEXT ] ; /*!< Context of destroyed hint extension. */
char exten_name [ AST_MAX_EXTENSION ] ; /*!< Extension of destroyed hint extension. */
char exten_name [ AST_MAX_EXTENSION ] ; /*!< Extension of destroyed hint extension. */
} ;
AST_VECTOR ( , char * ) devices ; /*!< Devices associated with the hint */
} ;
# define HINTDEVICE_DATA_LENGTH 16
# define HINTDEVICE_DATA_LENGTH 16
AST_THREADSTORAGE ( hintdevice_data ) ;
AST_THREADSTORAGE ( hintdevice_data ) ;
@ -1077,15 +1079,28 @@ struct ast_hintdevice {
char hintdevice [ 1 ] ;
char hintdevice [ 1 ] ;
} ;
} ;
/*!
/*!
* \ note Using the device for hash
* \ note Using the device for hash
*/
*/
static int hintdevice_hash_cb ( const void * obj , const int flags )
static int hintdevice_hash_cb ( const void * obj , const int flags )
{
{
const struct ast_hintdevice * ext = obj ;
const struct ast_hintdevice * ext ;
const char * key ;
return ast_str_case_hash ( ext - > hintdevice ) ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_KEY :
key = obj ;
break ;
case OBJ_SEARCH_OBJECT :
ext = obj ;
key = ext - > hintdevice ;
break ;
default :
ast_assert ( 0 ) ;
return 0 ;
}
return ast_str_case_hash ( key ) ;
}
}
/*!
/*!
* \ note Devices on hints are not unique so no CMP_STOP is returned
* \ note Devices on hints are not unique so no CMP_STOP is returned
@ -1094,29 +1109,58 @@ static int hintdevice_hash_cb(const void *obj, const int flags)
*/
*/
static int hintdevice_cmp_multiple ( void * obj , void * arg , int flags )
static int hintdevice_cmp_multiple ( void * obj , void * arg , int flags )
{
{
struct ast_hintdevice * ext = obj , * ext2 = arg ;
struct ast_hintdevice * left = obj ;
struct ast_hintdevice * right = arg ;
const char * right_key = arg ;
int cmp ;
return ! strcasecmp ( ext - > hintdevice , ext2 - > hintdevice ) ? CMP_MATCH : 0 ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_OBJECT :
right_key = right - > hintdevice ;
/* Fall through */
case OBJ_SEARCH_KEY :
cmp = strcmp ( left - > hintdevice , 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 ( left - > hintdevice , right_key , strlen ( right_key ) ) ;
break ;
default :
ast_assert ( 0 ) ;
cmp = 0 ;
break ;
}
return cmp ? 0 : CMP_MATCH ;
}
}
/*
/*! \internal \brief \c ao2_callback function to remove hintdevices */
* \ details This is used with ao2_callback to remove old devices
static int hintdevice_remove_cb ( void * obj , void * arg , void * data , int flags )
* when they are linked to the hint
*/
static int hintdevice_remove_cb ( void * deviceobj , void * arg , int flags )
{
{
struct ast_hintdevice * device = deviceobj ;
struct ast_hintdevice * candidate = obj ;
struct ast_hint * hint = arg ;
char * device = arg ;
struct ast_hint * hint = data ;
return ( device - > hint = = hint ) ? CMP_MATCH : 0 ;
if ( ! strcmp ( candidate - > hintdevice , device )
& & candidate - > hint = = hint ) {
return CMP_MATCH ;
}
return 0 ;
}
}
static int remove_hintdevice ( struct ast_hint * hint )
static int remove_hintdevice ( struct ast_hint * hint )
{
{
/* iterate through all devices and remove the devices which are linked to this hint */
while ( AST_VECTOR_SIZE ( & hint - > devices ) > 0 ) {
ao2_t_callback ( hintdevices , OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK ,
char * device = AST_VECTOR_GET ( & hint - > devices , 0 ) ;
hintdevice_remove_cb , hint ,
" callback to remove all devices which are linked to a hint " ) ;
ao2_t_callback_data ( hintdevices , OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA ,
hintdevice_remove_cb , device , hint , " Remove device from container " ) ;
AST_VECTOR_REMOVE_UNORDERED ( & hint - > devices , 0 ) ;
ast_free ( device ) ;
}
return 0 ;
return 0 ;
}
}
@ -1161,18 +1205,32 @@ static int add_hintdevice(struct ast_hint *hint, const char *devicelist)
/* Spit on '&' and ',' to handle presence hints as well */
/* Spit on '&' and ',' to handle presence hints as well */
while ( ( cur = strsep ( & parse , " &, " ) ) ) {
while ( ( cur = strsep ( & parse , " &, " ) ) ) {
char * device_name ;
devicelength = strlen ( cur ) ;
devicelength = strlen ( cur ) ;
if ( ! devicelength ) {
if ( ! devicelength ) {
continue ;
continue ;
}
}
device_name = ast_strdup ( cur ) ;
if ( ! device_name ) {
return - 1 ;
}
device = ao2_t_alloc ( sizeof ( * device ) + devicelength , hintdevice_destroy ,
device = ao2_t_alloc ( sizeof ( * device ) + devicelength , hintdevice_destroy ,
" allocating a hintdevice structure " ) ;
" allocating a hintdevice structure " ) ;
if ( ! device ) {
if ( ! device ) {
ast_free ( device_name ) ;
return - 1 ;
return - 1 ;
}
}
strcpy ( device - > hintdevice , cur ) ;
strcpy ( device - > hintdevice , cur ) ;
ao2_ref ( hint , + 1 ) ;
ao2_ref ( hint , + 1 ) ;
device - > hint = hint ;
device - > hint = hint ;
if ( AST_VECTOR_APPEND ( & hint - > devices , device_name ) ) {
ast_free ( device_name ) ;
ao2_ref ( device , - 1 ) ;
return - 1 ;
}
ao2_t_link ( hintdevices , device , " Linking device into hintdevice container. " ) ;
ao2_t_link ( hintdevices , device , " Linking device into hintdevice container. " ) ;
ao2_t_ref ( device , - 1 , " hintdevice is linked so we can unref " ) ;
ao2_t_ref ( device , - 1 , " hintdevice is linked so we can unref " ) ;
}
}
@ -5385,7 +5443,7 @@ static void device_state_cb(void *unused, struct stasis_subscription *sub, struc
ast_mutex_lock ( & context_merge_lock ) ; /* Hold off ast_merge_contexts_and_delete */
ast_mutex_lock ( & context_merge_lock ) ; /* Hold off ast_merge_contexts_and_delete */
dev_iter = ao2_t_callback ( hintdevices ,
dev_iter = ao2_t_callback ( hintdevices ,
OBJ_ POINTER | OBJ_MULTIPLE ,
OBJ_ SEARCH_OBJECT | OBJ_MULTIPLE ,
hintdevice_cmp_multiple ,
hintdevice_cmp_multiple ,
cmpdevice ,
cmpdevice ,
" find devices in container " ) ;
" find devices in container " ) ;
@ -5697,6 +5755,7 @@ static int hint_id_cmp(void *obj, void *arg, int flags)
static void destroy_hint ( void * obj )
static void destroy_hint ( void * obj )
{
{
struct ast_hint * hint = obj ;
struct ast_hint * hint = obj ;
int i ;
if ( hint - > callbacks ) {
if ( hint - > callbacks ) {
struct ast_state_cb * state_cb ;
struct ast_state_cb * state_cb ;
@ -5726,6 +5785,12 @@ static void destroy_hint(void *obj)
}
}
ao2_ref ( hint - > callbacks , - 1 ) ;
ao2_ref ( hint - > callbacks , - 1 ) ;
}
}
for ( i = 0 ; i < AST_VECTOR_SIZE ( & hint - > devices ) ; i + + ) {
char * device = AST_VECTOR_GET ( & hint - > devices , i ) ;
ast_free ( device ) ;
}
AST_VECTOR_FREE ( & hint - > devices ) ;
ast_free ( hint - > last_presence_subtype ) ;
ast_free ( hint - > last_presence_subtype ) ;
ast_free ( hint - > last_presence_message ) ;
ast_free ( hint - > last_presence_message ) ;
}
}
@ -5787,6 +5852,7 @@ static int ast_add_hint(struct ast_exten *e)
if ( ! hint_new ) {
if ( ! hint_new ) {
return - 1 ;
return - 1 ;
}
}
AST_VECTOR_INIT ( & hint_new - > devices , 8 ) ;
/* Initialize new hint. */
/* Initialize new hint. */
hint_new - > callbacks = ao2_container_alloc ( 1 , NULL , hint_id_cmp ) ;
hint_new - > callbacks = ao2_container_alloc ( 1 , NULL , hint_id_cmp ) ;
@ -12526,14 +12592,17 @@ static int statecbs_cmp(void *obj, void *arg, int flags)
static void pbx_shutdown ( void )
static void pbx_shutdown ( void )
{
{
if ( hints ) {
if ( hints ) {
ao2_container_unregister ( " hints " ) ;
ao2_ref ( hints , - 1 ) ;
ao2_ref ( hints , - 1 ) ;
hints = NULL ;
hints = NULL ;
}
}
if ( hintdevices ) {
if ( hintdevices ) {
ao2_container_unregister ( " hintdevices " ) ;
ao2_ref ( hintdevices , - 1 ) ;
ao2_ref ( hintdevices , - 1 ) ;
hintdevices = NULL ;
hintdevices = NULL ;
}
}
if ( statecbs ) {
if ( statecbs ) {
ao2_container_unregister ( " statecbs " ) ;
ao2_ref ( statecbs , - 1 ) ;
ao2_ref ( statecbs , - 1 ) ;
statecbs = NULL ;
statecbs = NULL ;
}
}
@ -12543,11 +12612,53 @@ static void pbx_shutdown(void)
pbx_builtin_clear_globals ( ) ;
pbx_builtin_clear_globals ( ) ;
}
}
static void print_hints_key ( void * v_obj , void * where , ao2_prnt_fn * prnt )
{
struct ast_hint * hint = v_obj ;
if ( ! hint ) {
return ;
}
prnt ( where , " %s@%s " , ast_get_extension_name ( hint - > exten ) ,
ast_get_context_name ( ast_get_extension_context ( hint - > exten ) ) ) ;
}
static void print_hintdevices_key ( void * v_obj , void * where , ao2_prnt_fn * prnt )
{
struct ast_hintdevice * hintdevice = v_obj ;
if ( ! hintdevice ) {
return ;
}
prnt ( where , " %s => %s@%s " , hintdevice - > hintdevice ,
ast_get_extension_name ( hintdevice - > hint - > exten ) ,
ast_get_context_name ( ast_get_extension_context ( hintdevice - > hint - > exten ) ) ) ;
}
static void print_statecbs_key ( void * v_obj , void * where , ao2_prnt_fn * prnt )
{
struct ast_state_cb * state_cb = v_obj ;
if ( ! state_cb ) {
return ;
}
prnt ( where , " %d " , state_cb - > id ) ;
}
int ast_pbx_init ( void )
int ast_pbx_init ( void )
{
{
hints = ao2_container_alloc ( HASH_EXTENHINT_SIZE , hint_hash , hint_cmp ) ;
hints = ao2_container_alloc ( HASH_EXTENHINT_SIZE , hint_hash , hint_cmp ) ;
if ( hints ) {
ao2_container_register ( " hints " , hints , print_hints_key ) ;
}
hintdevices = ao2_container_alloc ( HASH_EXTENHINT_SIZE , hintdevice_hash_cb , hintdevice_cmp_multiple ) ;
hintdevices = ao2_container_alloc ( HASH_EXTENHINT_SIZE , hintdevice_hash_cb , hintdevice_cmp_multiple ) ;
if ( hintdevices ) {
ao2_container_register ( " hintdevices " , hintdevices , print_hintdevices_key ) ;
}
statecbs = ao2_container_alloc ( 1 , NULL , statecbs_cmp ) ;
statecbs = ao2_container_alloc ( 1 , NULL , statecbs_cmp ) ;
if ( statecbs ) {
ao2_container_register ( " statecbs " , statecbs , print_statecbs_key ) ;
}
ast_register_cleanup ( pbx_shutdown ) ;
ast_register_cleanup ( pbx_shutdown ) ;