@ -43,6 +43,7 @@ ASTERISK_REGISTER_FILE()
# include "asterisk/taskprocessor.h"
# include "asterisk/threadpool.h"
# include "asterisk/json.h"
# include "asterisk/vector.h"
/* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
# undef open
@ -86,6 +87,35 @@ ASTERISK_REGISTER_FILE()
/*! \brief Thread pool for observers */
static struct ast_threadpool * threadpool ;
/*! \brief Structure for an internal wizard instance */
struct ast_sorcery_internal_wizard {
/*!
* \ brief Wizard interface itself
* \ warning Callbacks must always be declared first in this structure
* so an ao2_ref on & callbacks will adjust the ref count on
* internal_wizard .
*/
struct ast_sorcery_wizard callbacks ;
/*! \brief Observers */
struct ao2_container * observers ;
} ;
/*! \brief Structure for a wizard instance which operates on objects */
struct ast_sorcery_object_wizard {
/*! \brief Wizard interface itself */
struct ast_sorcery_internal_wizard * wizard ;
/*! \brief Unique data for the wizard */
void * data ;
/*! \brief Wizard is acting as an object cache */
unsigned int caching : 1 ;
} ;
/*! \brief Interface for a sorcery object type wizards */
AST_VECTOR_RW ( ast_sorcery_object_wizards , struct ast_sorcery_object_wizard * ) ;
/*! \brief Structure for internal sorcery object information */
struct ast_sorcery_object {
/*! \brief Unique identifier of this object */
@ -119,7 +149,7 @@ struct ast_sorcery_object_type {
sorcery_diff_handler diff ;
/*! \brief Wizard instances */
struct a o2_container * wizards ;
struct a st_sorcery_object_wizards wizards ;
/*! \brief Object fields */
struct ao2_container * fields ;
@ -176,27 +206,6 @@ struct ast_sorcery_object_field {
intptr_t args [ ] ;
} ;
/*! \brief Structure for an internal wizard instance */
struct ast_sorcery_internal_wizard {
/*! \brief Wizard interface itself */
struct ast_sorcery_wizard callbacks ;
/*! \brief Observers */
struct ao2_container * observers ;
} ;
/*! \brief Structure for a wizard instance which operates on objects */
struct ast_sorcery_object_wizard {
/*! \brief Wizard interface itself */
struct ast_sorcery_internal_wizard * wizard ;
/*! \brief Unique data for the wizard */
void * data ;
/*! \brief Wizard is acting as an object cache */
unsigned int caching : 1 ;
} ;
/*! \brief Full structure for sorcery */
struct ast_sorcery {
/*! \brief Container for known object types */
@ -789,7 +798,10 @@ static void sorcery_object_type_destructor(void *obj)
{
struct ast_sorcery_object_type * object_type = obj ;
ao2_cleanup ( object_type - > wizards ) ;
AST_VECTOR_RW_WRLOCK ( & object_type - > wizards ) ;
AST_VECTOR_CALLBACK_VOID ( & object_type - > wizards , ao2_cleanup ) ;
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
AST_VECTOR_RW_FREE ( & object_type - > wizards ) ;
ao2_cleanup ( object_type - > fields ) ;
ao2_cleanup ( object_type - > observers ) ;
@ -806,6 +818,7 @@ static void sorcery_object_type_destructor(void *obj)
/*! \brief Internal function which allocates an object type structure */
static struct ast_sorcery_object_type * sorcery_object_type_alloc ( const char * type , const char * module )
{
# define INITIAL_WIZARD_VECTOR_SIZE 5
struct ast_sorcery_object_type * object_type ;
char uuid [ AST_UUID_STR_LEN ] ;
@ -814,7 +827,7 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
}
/* Order matters for object wizards */
if ( ! ( object_type - > wizards = ao2_container_alloc_options ( AO2_ALLOC_OPT_LOCK_NOLOCK , 1 , NULL , sorcery_wizard_cmp ) ) ) {
if ( AST_VECTOR_RW_INIT ( & object_type - > wizards , INITIAL_WIZARD_VECTOR_SIZE ) ! = 0 ) {
ao2_ref ( object_type , - 1 ) ;
return NULL ;
}
@ -864,7 +877,7 @@ static void sorcery_object_wizard_destructor(void *obj)
{
struct ast_sorcery_object_wizard * object_wizard = obj ;
if ( object_wizard - > data ) {
if ( object_wizard - > data & & object_wizard - > wizard - > callbacks . close ) {
object_wizard - > wizard - > callbacks . close ( object_wizard - > data ) ;
}
@ -875,9 +888,73 @@ static void sorcery_object_wizard_destructor(void *obj)
ao2_cleanup ( object_wizard - > wizard ) ;
}
/*! \brief Internal function which creates an object type and adds a wizard mapping */
enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping ( struct ast_sorcery * sorcery ,
const char * type , const char * module , const char * name , const char * data , unsigned int caching )
/*! \brief Return the number of wizards mapped to an object type */
int ast_sorcery_get_wizard_mapping_count ( struct ast_sorcery * sorcery ,
const char * type )
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
if ( ! object_type ) {
return - 1 ;
}
return AST_VECTOR_SIZE ( & object_type - > wizards ) ;
}
int ast_sorcery_get_wizard_mapping ( struct ast_sorcery * sorcery ,
const char * type , int index , struct ast_sorcery_wizard * * wizard , void * * data )
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
struct ast_sorcery_object_wizard * owizard ;
if ( ! object_type ) {
return - 1 ;
}
if ( index < 0 | | index > = AST_VECTOR_SIZE ( & object_type - > wizards ) ) {
return - 1 ;
}
owizard = AST_VECTOR_GET ( & object_type - > wizards , index ) ;
if ( wizard ! = NULL ) {
* wizard = & ( owizard - > wizard - > callbacks ) ;
ao2_bump ( owizard - > wizard ) ;
} else {
return - 1 ;
}
if ( data ! = NULL ) {
* data = owizard - > data ;
}
return 0 ;
}
/*! \brief Internal function removes a wizard mapping */
int __ast_sorcery_remove_wizard_mapping ( struct ast_sorcery * sorcery ,
const char * type , const char * module , const char * name )
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
int res ;
if ( ! object_type ) {
return - 1 ;
}
AST_VECTOR_RW_WRLOCK ( & object_type - > wizards ) ;
# define WIZARD_NAME_COMPARE(a, b) (strcmp((a)->wizard->callbacks.name, (b)) == 0)
res = AST_VECTOR_REMOVE_CMP_ORDERED ( & object_type - > wizards , name , WIZARD_NAME_COMPARE , ao2_cleanup ) ;
# undef WIZARD_NAME_COMPARE
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return res ;
}
/*! \brief Internal function which creates an object type and inserts a wizard mapping */
enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping ( struct ast_sorcery * sorcery ,
const char * type , const char * module , const char * name , const char * data ,
unsigned int caching , int position )
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
RAII_VAR ( struct ast_sorcery_internal_wizard * , wizard , ao2_find ( wizards , name , OBJ_KEY ) , ao2_cleanup ) ;
@ -899,19 +976,23 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
created = 1 ;
}
AST_VECTOR_RW_WRLOCK ( & object_type - > wizards ) ;
if ( ! created ) {
struct ast_sorcery_ wizard * found ;
struct ast_sorcery_ object_ wizard * found ;
found = ao2_find ( object_type - > wizards , wizard , OBJ_SEARCH_OBJECT ) ;
# define WIZARD_COMPARE(a, b) ((a)->wizard == (b))
found = AST_VECTOR_GET_CMP ( & object_type - > wizards , wizard , WIZARD_COMPARE ) ;
# undef WIZARD_COMPARE
if ( found ) {
ast_debug ( 1 , " Wizard %s already applied to object type %s \n " ,
wizard - > callbacks . name , object_type - > name ) ;
ao2_cleanup( found ) ;
AST_VECTOR_RW_UNLOCK( & object_type - > wizards ) ;
return AST_SORCERY_APPLY_DUPLICATE ;
}
}
if ( wizard - > callbacks . open & & ! ( object_wizard - > data = wizard - > callbacks . open ( data ) ) ) {
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return AST_SORCERY_APPLY_FAIL ;
}
@ -920,7 +1001,16 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
object_wizard - > wizard = ao2_bump ( wizard ) ;
object_wizard - > caching = caching ;
ao2_link ( object_type - > wizards , object_wizard ) ;
if ( position = = AST_SORCERY_WIZARD_POSITION_LAST ) {
position = AST_VECTOR_SIZE ( & object_type - > wizards ) ;
}
if ( AST_VECTOR_INSERT_AT ( & object_type - > wizards , position , object_wizard ) ! = 0 ) {
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return AST_SORCERY_APPLY_FAIL ;
}
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
ao2_bump ( object_wizard ) ;
if ( created ) {
ao2_link ( sorcery - > types , object_type ) ;
@ -932,6 +1022,14 @@ enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorc
return AST_SORCERY_APPLY_SUCCESS ;
}
/*! \brief Internal function which creates an object type and adds a wizard mapping */
enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping ( struct ast_sorcery * sorcery ,
const char * type , const char * module , const char * name , const char * data , unsigned int caching )
{
return __ast_sorcery_insert_wizard_mapping ( sorcery , type , module , name , data ,
caching , AST_SORCERY_WIZARD_POSITION_LAST ) ;
}
enum ast_sorcery_apply_result __ast_sorcery_apply_config ( struct ast_sorcery * sorcery , const char * name , const char * module )
{
struct ast_flags flags = { 0 } ;
@ -1260,7 +1358,9 @@ static int sorcery_object_load(void *obj, void *arg, int flags)
NOTIFY_INSTANCE_OBSERVERS ( details - > sorcery - > observers , object_type_loading ,
details - > sorcery - > module_name , details - > sorcery , type - > name , details - > reload ) ;
ao2_callback ( type - > wizards , OBJ_NODATA , sorcery_wizard_load , details ) ;
AST_VECTOR_RW_RDLOCK ( & type - > wizards ) ;
AST_VECTOR_CALLBACK ( & type - > wizards , sorcery_wizard_load , NULL , details , 0 ) ;
AST_VECTOR_RW_UNLOCK ( & type - > wizards ) ;
NOTIFY_INSTANCE_OBSERVERS ( details - > sorcery - > observers , object_type_loaded ,
details - > sorcery - > module_name , details - > sorcery , type - > name , details - > reload ) ;
@ -1700,31 +1800,31 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
void * object = NULL ;
struct ao2_iterator i ;
struct ast_sorcery_object_wizard * wizard ;
int i ;
unsigned int cached = 0 ;
if ( ! object_type | | ast_strlen_zero ( id ) ) {
return NULL ;
}
i = ao2_iterator_init ( object_type - > wizards , 0 ) ;
for ( ; ( wizard = ao2_iterator_next ( & i ) ) ; ao2_ref ( wizard , - 1 ) ) {
AST_VECTOR_RW_RDLOCK ( & object_type - > wizards ) ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & object_type - > wizards ) ; i + + ) {
struct ast_sorcery_object_wizard * wizard =
AST_VECTOR_GET ( & object_type - > wizards , i ) ;
if ( wizard - > wizard - > callbacks . retrieve_id & &
! ( object = wizard - > wizard - > callbacks . retrieve_id ( sorcery , wizard - > data , object_type - > name , id ) ) ) {
continue ;
}
cached = wizard - > caching ;
ao2_ref ( wizard , - 1 ) ;
break ;
}
ao2_iterator_destroy ( & i ) ;
if ( ! cached & & object ) {
ao2_callback( object_type - > wizards , 0 , sorcery_cache_create , object ) ;
AST_VECTOR_CALLBACK( & object_type - > wizards , sorcery_cache_create , NULL , object , 0 ) ;
}
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return object ;
}
@ -1733,8 +1833,7 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
void * object = NULL ;
struct ao2_iterator i ;
struct ast_sorcery_object_wizard * wizard ;
int i ;
unsigned int cached = 0 ;
if ( ! object_type ) {
@ -1748,9 +1847,11 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
}
}
/* Inquire with the available wizards for retrieval */
i = ao2_iterator_init ( object_type - > wizards , 0 ) ;
for ( ; ( wizard = ao2_iterator_next ( & i ) ) ; ao2_ref ( wizard , - 1 ) ) {
AST_VECTOR_RW_RDLOCK ( & object_type - > wizards ) ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & object_type - > wizards ) ; i + + ) {
struct ast_sorcery_object_wizard * wizard =
AST_VECTOR_GET ( & object_type - > wizards , i ) ;
if ( ( flags & AST_RETRIEVE_FLAG_MULTIPLE ) ) {
if ( wizard - > wizard - > callbacks . retrieve_multiple ) {
wizard - > wizard - > callbacks . retrieve_multiple ( sorcery , wizard - > data , object_type - > name , object , fields ) ;
@ -1767,15 +1868,14 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
cached = wizard - > caching ;
ao2_ref ( wizard , - 1 ) ;
break ;
}
ao2_iterator_destroy ( & i ) ;
/* If we are returning a single object and it came from a non-cache source create it in any caches */
if ( ! ( flags & AST_RETRIEVE_FLAG_MULTIPLE ) & & ! cached & & object ) {
ao2_callback( object_type - > wizards , 0 , sorcery_cache_create , object ) ;
AST_VECTOR_CALLBACK( & object_type - > wizards , sorcery_cache_create , NULL , object , 0 ) ;
}
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return object ;
}
@ -1784,22 +1884,24 @@ struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *so
{
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , type , OBJ_KEY ) , ao2_cleanup ) ;
struct ao2_container * objects ;
struct ao2_iterator i ;
struct ast_sorcery_object_wizard * wizard ;
int i ;
if ( ! object_type | | ! ( objects = ao2_container_alloc_options ( AO2_ALLOC_OPT_LOCK_NOLOCK , 1 , NULL , NULL ) ) ) {
return NULL ;
}
i = ao2_iterator_init ( object_type - > wizards , 0 ) ;
for ( ; ( wizard = ao2_iterator_next ( & i ) ) ; ao2_ref ( wizard , - 1 ) ) {
AST_VECTOR_RW_RDLOCK ( & object_type - > wizards ) ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & object_type - > wizards ) ; i + + ) {
struct ast_sorcery_object_wizard * wizard =
AST_VECTOR_GET ( & object_type - > wizards , i ) ;
if ( ! wizard - > wizard - > callbacks . retrieve_regex ) {
continue ;
}
wizard - > wizard - > callbacks . retrieve_regex ( sorcery , wizard - > data , object_type - > name , objects , regex ) ;
}
ao2_iterator_destroy( & i ) ;
AST_VECTOR_RW_UNLOCK( & object_type - > wizards ) ;
return objects ;
}
@ -1846,7 +1948,9 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details * details = object ;
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , details - > object - > type , OBJ_KEY ) , ao2_cleanup ) ;
RAII_VAR ( struct ast_sorcery_object_wizard * , object_wizard , NULL , ao2_cleanup ) ;
struct ast_sorcery_object_wizard * object_wizard = NULL ;
struct ast_sorcery_object_wizard * found_wizard ;
int i ;
struct sorcery_details sdetails = {
. sorcery = sorcery ,
. obj = object ,
@ -1856,14 +1960,21 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
return - 1 ;
}
if ( ( object_wizard = ao2_callback ( object_type - > wizards , 0 , sorcery_wizard_create , & sdetails ) ) & &
ao2_container_count ( object_type - > observers ) ) {
struct sorcery_observer_invocation * invocation = sorcery_observer_invocation_alloc ( object_type , object ) ;
AST_VECTOR_RW_RDLOCK ( & object_type - > wizards ) ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & object_type - > wizards ) ; i + + ) {
found_wizard = AST_VECTOR_GET ( & object_type - > wizards , i ) ;
if ( sorcery_wizard_create ( found_wizard , & sdetails , 0 ) = = ( CMP_MATCH | CMP_STOP ) ) {
object_wizard = found_wizard ;
if ( ao2_container_count ( object_type - > observers ) ) {
struct sorcery_observer_invocation * invocation = sorcery_observer_invocation_alloc ( object_type , object ) ;
if ( invocation & & ast_taskprocessor_push ( object_type - > serializer , sorcery_observers_notify_create , invocation ) ) {
ao2_cleanup ( invocation ) ;
if ( invocation & & ast_taskprocessor_push ( object_type - > serializer , sorcery_observers_notify_create , invocation ) ) {
ao2_cleanup ( invocation ) ;
}
}
}
}
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return object_wizard ? 0 : - 1 ;
}
@ -1905,7 +2016,9 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details * details = object ;
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , details - > object - > type , OBJ_KEY ) , ao2_cleanup ) ;
RAII_VAR ( struct ast_sorcery_object_wizard * , object_wizard , NULL , ao2_cleanup ) ;
struct ast_sorcery_object_wizard * object_wizard = NULL ;
struct ast_sorcery_object_wizard * found_wizard ;
int i ;
struct sorcery_details sdetails = {
. sorcery = sorcery ,
. obj = object ,
@ -1915,14 +2028,21 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
return - 1 ;
}
if ( ( object_wizard = ao2_callback ( object_type - > wizards , 0 , sorcery_wizard_update , & sdetails ) ) & &
ao2_container_count ( object_type - > observers ) ) {
struct sorcery_observer_invocation * invocation = sorcery_observer_invocation_alloc ( object_type , object ) ;
AST_VECTOR_RW_RDLOCK ( & object_type - > wizards ) ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & object_type - > wizards ) ; i + + ) {
found_wizard = AST_VECTOR_GET ( & object_type - > wizards , i ) ;
if ( sorcery_wizard_update ( found_wizard , & sdetails , 0 ) = = ( CMP_MATCH | CMP_STOP ) ) {
object_wizard = found_wizard ;
if ( ao2_container_count ( object_type - > observers ) ) {
struct sorcery_observer_invocation * invocation = sorcery_observer_invocation_alloc ( object_type , object ) ;
if ( invocation & & ast_taskprocessor_push ( object_type - > serializer , sorcery_observers_notify_update , invocation ) ) {
ao2_cleanup ( invocation ) ;
if ( invocation & & ast_taskprocessor_push ( object_type - > serializer , sorcery_observers_notify_update , invocation ) ) {
ao2_cleanup ( invocation ) ;
}
}
}
}
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return object_wizard ? 0 : - 1 ;
}
@ -1964,7 +2084,9 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details * details = object ;
RAII_VAR ( struct ast_sorcery_object_type * , object_type , ao2_find ( sorcery - > types , details - > object - > type , OBJ_KEY ) , ao2_cleanup ) ;
RAII_VAR ( struct ast_sorcery_object_wizard * , object_wizard , NULL , ao2_cleanup ) ;
struct ast_sorcery_object_wizard * object_wizard = NULL ;
struct ast_sorcery_object_wizard * found_wizard ;
int i ;
struct sorcery_details sdetails = {
. sorcery = sorcery ,
. obj = object ,
@ -1974,14 +2096,21 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
return - 1 ;
}
if ( ( object_wizard = ao2_callback ( object_type - > wizards , 0 , sorcery_wizard_delete , & sdetails ) ) & &
ao2_container_count ( object_type - > observers ) ) {
struct sorcery_observer_invocation * invocation = sorcery_observer_invocation_alloc ( object_type , object ) ;
AST_VECTOR_RW_RDLOCK ( & object_type - > wizards ) ;
for ( i = 0 ; i < AST_VECTOR_SIZE ( & object_type - > wizards ) ; i + + ) {
found_wizard = AST_VECTOR_GET ( & object_type - > wizards , i ) ;
if ( sorcery_wizard_delete ( found_wizard , & sdetails , 0 ) = = ( CMP_MATCH | CMP_STOP ) ) {
object_wizard = found_wizard ;
if ( ao2_container_count ( object_type - > observers ) ) {
struct sorcery_observer_invocation * invocation = sorcery_observer_invocation_alloc ( object_type , object ) ;
if ( invocation & & ast_taskprocessor_push ( object_type - > serializer , sorcery_observers_notify_delete , invocation ) ) {
ao2_cleanup ( invocation ) ;
if ( invocation & & ast_taskprocessor_push ( object_type - > serializer , sorcery_observers_notify_delete , invocation ) ) {
ao2_cleanup ( invocation ) ;
}
}
}
}
AST_VECTOR_RW_UNLOCK ( & object_type - > wizards ) ;
return object_wizard ? 0 : - 1 ;
}