diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/bridge_channel.c | 264 | ||||
-rw-r--r-- | main/channel.c | 2 | ||||
-rw-r--r-- | main/frame.c | 8 | ||||
-rw-r--r-- | main/sorcery.c | 47 |
4 files changed, 287 insertions, 34 deletions
diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 06da4eeed..75008fe18 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -35,6 +35,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <signal.h> +#include <semaphore.h> #include "asterisk/heap.h" #include "asterisk/astobj2.h" @@ -70,6 +71,142 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") */ typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen); +/*! + * \brief Counter used for assigning synchronous bridge action IDs + */ +static int sync_ids; + +/*! + * \brief Frame payload for synchronous bridge actions. + * + * The payload serves as a wrapper around the actual payload of the + * frame, with the addition of an id used to find the associated + * bridge_sync object. + */ +struct sync_payload { + /*! Unique ID for this synchronous action */ + unsigned int id; + /*! Actual frame data to process */ + unsigned char data[0]; +}; + +/*! + * \brief Synchronous bridge action object. + * + * Synchronous bridge actions require the ability for one thread to wait + * and for another thread to indicate that the action has completed. This + * structure facilitates that goal by providing synchronization structures. + */ +struct bridge_sync { + /*! Unique ID of this synchronization object. Corresponds with ID in synchronous frame payload */ + unsigned int id; + /*! Semaphore used for synchronization */ + sem_t sem; + /*! Pointer to next entry in the list */ + AST_LIST_ENTRY(bridge_sync) list; +}; + +/*! + * \brief List holding active synchronous action objects. + */ +static AST_RWLIST_HEAD_STATIC(sync_structs, bridge_sync); + +/*! + * \brief initialize a synchronous bridge object. + * + * This both initializes the structure and adds it to the list of + * synchronization structures. + * + * \param sync_struct The synchronization object to initialize. + * \param id ID to assign to the synchronization object. + */ +static void bridge_sync_init(struct bridge_sync *sync_struct, unsigned int id) +{ + memset(sync_struct, 0, sizeof(*sync_struct)); + sync_struct->id = id; + sem_init(&sync_struct->sem, 0, 0); + + AST_RWLIST_WRLOCK(&sync_structs); + AST_RWLIST_INSERT_TAIL(&sync_structs, sync_struct, list); + AST_RWLIST_UNLOCK(&sync_structs); +} + +/*! + * \brief Clean up a syncrhonization bridge object. + * + * This frees fields within the synchronization object and removes + * it from the list of active synchronization objects. + * + * Since synchronization objects are stack-allocated, it is vital + * that this is called before the synchronization object goes + * out of scope. + * + * \param sync_struct Synchronization object to clean up. + */ +static void bridge_sync_cleanup(struct bridge_sync *sync_struct) +{ + struct bridge_sync *iter; + + AST_RWLIST_WRLOCK(&sync_structs); + AST_LIST_TRAVERSE_SAFE_BEGIN(&sync_structs, iter, list) { + if (iter->id == sync_struct->id) { + AST_LIST_REMOVE_CURRENT(list); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&sync_structs); + + sem_destroy(&sync_struct->sem); +} + +/*! + * \brief Failsafe for synchronous bridge action waiting. + * + * When waiting for a synchronous bridge action to complete, + * if there is a frame resource leak somewhere, it is possible + * that we will never get notified that the synchronous action + * completed. + * + * If a significant amount of time passes, then we will abandon + * waiting for the synchrnous bridge action to complete. + * + * This constant represents the number of milliseconds we will + * wait for the bridge action to complete. + */ +#define PLAYBACK_TIMEOUT (600 * 1000) + +/*! + * \brief Wait for a synchronous bridge action to complete. + * + * \param sync_struct Synchronization object corresponding to the bridge action. + */ +static void bridge_sync_wait(struct bridge_sync *sync_struct) +{ + struct timeval timeout_val = ast_tvadd(ast_tvnow(), ast_samp2tv(PLAYBACK_TIMEOUT, 1000)); + struct timespec timeout_spec = { + .tv_sec = timeout_val.tv_sec, + .tv_nsec = timeout_val.tv_usec * 1000, + }; + + sem_timedwait(&sync_struct->sem, &timeout_spec); +} + +/*! + * \brief Signal that waiting for a synchronous bridge action is no longer necessary. + * + * This may occur for several reasons + * \li The synchronous bridge action has completed. + * \li The bridge channel has been removed from the bridge. + * \li The synchronous bridge action could not be queued. + * + * \param sync_struct Synchronization object corresponding to the bridge action. + */ +static void bridge_sync_signal(struct bridge_sync *sync_struct) +{ + sem_post(&sync_struct->sem); +} + void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge; @@ -342,6 +479,8 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int caus */ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { + ast_assert(frame->frametype != AST_FRAME_BRIDGE_ACTION_SYNC); + ast_bridge_channel_lock_bridge(bridge_channel); /* * XXX need to implement a deferred write queue for when there @@ -493,7 +632,8 @@ static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel) * \retval 0 on success. * \retval -1 on error. */ -static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) +static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, + enum bridge_channel_action_type action, const void *data, size_t datalen) { struct ast_frame frame = { .frametype = AST_FRAME_BRIDGE_ACTION, @@ -507,6 +647,52 @@ static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_ch /*! * \internal + * \brief Queue an action frame onto the bridge channel with data synchronously. + * \since 12.2.0 + * + * The function will not return until the queued frame is freed. + * + * \param bridge_channel Which channel to queue the frame onto. + * \param action Type of bridge action frame. + * \param data Frame payload data to pass. + * \param datalen Frame payload data length to pass. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int bridge_channel_queue_action_data_sync(struct ast_bridge_channel *bridge_channel, + enum bridge_channel_action_type action, const void *data, size_t datalen) +{ + struct sync_payload *sync_payload; + int sync_payload_len = sizeof(*sync_payload) + datalen; + struct bridge_sync sync_struct; + struct ast_frame frame = { + .frametype = AST_FRAME_BRIDGE_ACTION_SYNC, + .subclass.integer = action, + }; + + /* Make sure we don't end up trying to wait on ourself to deliver the frame */ + ast_assert(!pthread_equal(pthread_self(), bridge_channel->thread)); + + sync_payload = ast_alloca(sync_payload_len); + sync_payload->id = ast_atomic_fetchadd_int(&sync_ids, +1); + memcpy(sync_payload->data, data, datalen); + + frame.datalen = sync_payload_len; + frame.data.ptr = sync_payload; + + bridge_sync_init(&sync_struct, sync_payload->id); + if (ast_bridge_channel_queue_frame(bridge_channel, &frame)) { + bridge_sync_cleanup(&sync_struct); + return -1; + } + + bridge_sync_wait(&sync_struct); + bridge_sync_cleanup(&sync_struct); + return 0; +} +/*! + * \internal * \brief Write an action frame onto the bridge channel with data. * \since 12.0.0 * @@ -518,7 +704,8 @@ static int bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_ch * \retval 0 on success. * \retval -1 on error. */ -static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) +static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, + enum bridge_channel_action_type action, const void *data, size_t datalen) { struct ast_frame frame = { .frametype = AST_FRAME_BRIDGE_ACTION, @@ -530,6 +717,27 @@ static int bridge_channel_write_action_data(struct ast_bridge_channel *bridge_ch return bridge_channel_write_frame(bridge_channel, &frame); } +static void bridge_frame_free(struct ast_frame *frame) +{ + if (frame->frametype == AST_FRAME_BRIDGE_ACTION_SYNC) { + struct sync_payload *sync_payload = frame->data.ptr; + struct bridge_sync *sync; + + AST_RWLIST_RDLOCK(&sync_structs); + AST_RWLIST_TRAVERSE(&sync_structs, sync, list) { + if (sync->id == sync_payload->id) { + break; + } + } + if (sync) { + bridge_sync_signal(sync); + } + AST_RWLIST_UNLOCK(&sync_structs); + } + + ast_frfree(frame); +} + int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) { struct ast_frame *dup; @@ -557,7 +765,7 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* Drop frames on channels leaving the bridge. */ ast_bridge_channel_unlock(bridge_channel); - ast_frfree(dup); + bridge_frame_free(dup); return 0; } @@ -816,7 +1024,7 @@ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, size_t len_payload = sizeof(*payload) + len_name + len_moh; /* Fill in play file frame data. */ - payload = alloca(len_payload); + payload = ast_alloca(len_payload); payload->custom_play = custom_play; payload->moh_offset = len_moh ? len_name : 0; strcpy(payload->playfile, playfile);/* Safe */ @@ -839,6 +1047,13 @@ int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, bridge_channel, custom_play, playfile, moh_class); } +int ast_bridge_channel_queue_playfile_sync(struct ast_bridge_channel *bridge_channel, + ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) +{ + return payload_helper_playfile(bridge_channel_queue_action_data_sync, + bridge_channel, custom_play, playfile, moh_class); +} + struct bridge_custom_callback { /*! Call this function on the bridge channel thread. */ ast_bridge_custom_callback_fn callback; @@ -1389,53 +1604,55 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c * * \param bridge_channel Channel to execute the action on. * \param action What to do. + * \param data data from the action. * * \return Nothing */ -static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action) +static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, + enum bridge_channel_action_type action, void *data) { - switch (action->subclass.integer) { + switch (action) { case BRIDGE_CHANNEL_ACTION_DTMF_STREAM: bridge_channel_suspend(bridge_channel); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_dtmf_stream(bridge_channel, action->data.ptr); + bridge_channel_dtmf_stream(bridge_channel, data); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; case BRIDGE_CHANNEL_ACTION_TALKING_START: case BRIDGE_CHANNEL_ACTION_TALKING_STOP: bridge_channel_talking(bridge_channel, - action->subclass.integer == BRIDGE_CHANNEL_ACTION_TALKING_START); + action == BRIDGE_CHANNEL_ACTION_TALKING_START); break; case BRIDGE_CHANNEL_ACTION_PLAY_FILE: bridge_channel_suspend(bridge_channel); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_playfile(bridge_channel, action->data.ptr); + bridge_channel_playfile(bridge_channel, data); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; case BRIDGE_CHANNEL_ACTION_RUN_APP: bridge_channel_suspend(bridge_channel); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_run_app(bridge_channel, action->data.ptr); + bridge_channel_run_app(bridge_channel, data); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; case BRIDGE_CHANNEL_ACTION_CALLBACK: - bridge_channel_do_callback(bridge_channel, action->data.ptr); + bridge_channel_do_callback(bridge_channel, data); break; case BRIDGE_CHANNEL_ACTION_PARK: bridge_channel_suspend(bridge_channel); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_park(bridge_channel, action->data.ptr); + bridge_channel_park(bridge_channel, data); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; case BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER: - bridge_channel_blind_transfer(bridge_channel, action->data.ptr); + bridge_channel_blind_transfer(bridge_channel, data); break; case BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER: - bridge_channel_attended_transfer(bridge_channel, action->data.ptr); + bridge_channel_attended_transfer(bridge_channel, data); break; default: break; @@ -1700,6 +1917,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe { struct ast_frame *fr; char nudge; + struct sync_payload *sync_payload; ast_bridge_channel_lock(bridge_channel); if (read(bridge_channel->alert_pipe[0], &nudge, sizeof(nudge)) < 0) { @@ -1715,7 +1933,11 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe } switch (fr->frametype) { case AST_FRAME_BRIDGE_ACTION: - bridge_channel_handle_action(bridge_channel, fr); + bridge_channel_handle_action(bridge_channel, fr->subclass.integer, fr->data.ptr); + break; + case AST_FRAME_BRIDGE_ACTION_SYNC: + sync_payload = fr->data.ptr; + bridge_channel_handle_action(bridge_channel, fr->subclass.integer, sync_payload->data); break; case AST_FRAME_CONTROL: bridge_channel_handle_control(bridge_channel, fr); @@ -1728,7 +1950,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe ast_write(bridge_channel->chan, fr); break; } - ast_frfree(fr); + bridge_frame_free(fr); } /*! \brief Internal function to handle DTMF from a channel */ @@ -1745,7 +1967,7 @@ static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_ch if (hook) { enum ast_frame_type frametype = frame->frametype; - ast_frfree(frame); + bridge_frame_free(frame); frame = NULL; ao2_ref(hook, -1); @@ -1805,7 +2027,7 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) switch (frame->subclass.integer) { case AST_CONTROL_HANGUP: ast_bridge_channel_kick(bridge_channel, 0); - ast_frfree(frame); + bridge_frame_free(frame); return; default: break; @@ -1818,7 +2040,7 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) return; } if (!bridge_channel->features->dtmf_passthrough) { - ast_frfree(frame); + bridge_frame_free(frame); return; } break; @@ -1828,7 +2050,7 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) /* Simply write the frame out to the bridge technology. */ bridge_channel_write_frame(bridge_channel, frame); - ast_frfree(frame); + bridge_frame_free(frame); } /*! @@ -2205,7 +2427,7 @@ static void bridge_channel_destroy(void *obj) /* Flush any unhandled wr_queue frames. */ while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) { - ast_frfree(fr); + bridge_frame_free(fr); } pipe_close(bridge_channel->alert_pipe); diff --git a/main/channel.c b/main/channel.c index 6145e3505..b264a2170 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1531,6 +1531,7 @@ int ast_is_deferrable_frame(const struct ast_frame *frame) */ switch (frame->frametype) { case AST_FRAME_BRIDGE_ACTION: + case AST_FRAME_BRIDGE_ACTION_SYNC: case AST_FRAME_CONTROL: case AST_FRAME_TEXT: case AST_FRAME_IMAGE: @@ -2875,6 +2876,7 @@ int __ast_answer(struct ast_channel *chan, unsigned int delay) case AST_FRAME_CONTROL: case AST_FRAME_IAX: case AST_FRAME_BRIDGE_ACTION: + case AST_FRAME_BRIDGE_ACTION_SYNC: case AST_FRAME_NULL: case AST_FRAME_CNG: break; diff --git a/main/frame.c b/main/frame.c index 8713ce40d..1b13abc69 100644 --- a/main/frame.c +++ b/main/frame.c @@ -639,6 +639,10 @@ void ast_frame_subclass2str(struct ast_frame *f, char *subclass, size_t slen, ch /* Should never happen */ snprintf(subclass, slen, "Bridge Frametype %d", f->subclass.integer); break; + case AST_FRAME_BRIDGE_ACTION_SYNC: + /* Should never happen */ + snprintf(subclass, slen, "Synchronous Bridge Frametype %d", f->subclass.integer); + break; case AST_FRAME_TEXT: ast_copy_string(subclass, "N/A", slen); if (moreinfo) { @@ -730,6 +734,10 @@ void ast_frame_type2str(enum ast_frame_type frame_type, char *ftype, size_t len) /* Should never happen */ ast_copy_string(ftype, "Bridge Specific", len); break; + case AST_FRAME_BRIDGE_ACTION_SYNC: + /* Should never happen */ + ast_copy_string(ftype, "Bridge Specific", len); + break; case AST_FRAME_TEXT: ast_copy_string(ftype, "Text", len); break; diff --git a/main/sorcery.c b/main/sorcery.c index 2ab4933be..94a11103b 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -580,6 +580,14 @@ struct ast_sorcery *__ast_sorcery_open(const char *module_name) } strcpy(sorcery->module_name, module_name); /* Safe */ + + if (__ast_sorcery_apply_config(sorcery, module_name, module_name) == AST_SORCERY_APPLY_FAIL) { + ast_log(LOG_ERROR, "Error attempting to apply configuration %s to sorcery.", module_name); + ao2_cleanup(sorcery); + sorcery = NULL; + goto done; + } + ao2_link_flags(instances, sorcery, OBJ_NOLOCK); done: @@ -623,7 +631,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, NULL))) { + if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) { ao2_ref(object_type, -1); return NULL; } @@ -683,7 +691,8 @@ static void sorcery_object_wizard_destructor(void *obj) } /*! \brief Internal function which creates an object type and adds a wizard mapping */ -static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data, unsigned int caching) +static enum ast_sorcery_apply_result sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, + const char *type, const char *module, const char *name, const char *data, unsigned int caching) { RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup); @@ -691,18 +700,30 @@ static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char int created = 0; if (!wizard || !object_wizard) { - return -1; + return AST_SORCERY_APPLY_FAIL; } if (!object_type) { if (!(object_type = sorcery_object_type_alloc(type, module))) { - return -1; + return AST_SORCERY_APPLY_FAIL; } created = 1; } + if (!created) { + struct ast_sorcery_wizard *found; + + found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT); + if (found) { + ast_debug(1, "Wizard %s already applied to object type %s\n", + wizard->name, object_type->name); + ao2_cleanup(found); + return AST_SORCERY_APPLY_DUPLICATE; + } + } + if (wizard->open && !(object_wizard->data = wizard->open(data))) { - return -1; + return AST_SORCERY_APPLY_FAIL; } ast_module_ref(wizard->module); @@ -716,18 +737,18 @@ static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char ao2_link(sorcery->types, object_type); } - return 0; + return AST_SORCERY_APPLY_SUCCESS; } -int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module) +enum ast_sorcery_apply_result __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module) { struct ast_flags flags = { 0 }; struct ast_config *config = ast_config_load2("sorcery.conf", "sorcery", flags); struct ast_variable *mapping; - int res = 0; + int res = AST_SORCERY_APPLY_SUCCESS; if (!config || config == CONFIG_STATUS_FILEINVALID) { - return -1; + return AST_SORCERY_APPLY_FAIL; } for (mapping = ast_variable_browse(config, name); mapping; mapping = mapping->next) { @@ -750,8 +771,8 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co } /* Any error immediately causes us to stop */ - if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching)) { - res = -1; + if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, data, caching) == AST_SORCERY_APPLY_FAIL) { + res = AST_SORCERY_APPLY_FAIL; break; } } @@ -761,13 +782,13 @@ int __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, co return res; } -int __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data) +enum ast_sorcery_apply_result __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, const char *module, const char *name, const char *data) { RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); /* Defaults can not be added if any existing mapping exists */ if (object_type) { - return -1; + return AST_SORCERY_APPLY_DEFAULT_UNNECESSARY; } return sorcery_apply_wizard_mapping(sorcery, type, module, name, data, 0); |