diff options
author | Matthew Jordan <mjordan@digium.com> | 2013-07-25 02:20:23 +0000 |
---|---|---|
committer | Matthew Jordan <mjordan@digium.com> | 2013-07-25 02:20:23 +0000 |
commit | 9d8a5ceb02f6559940bd94aaf163a544ce6e6f4c (patch) | |
tree | d5bc0d582e66aa19f1ec14767edbd83bdfbb7b63 /main | |
parent | 71609d58aa8e8cd9d0cbc4e2a29ab20d19599c27 (diff) |
Move after bridge callbacks into their own file
One more major refactoring to go.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395367 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r-- | main/bridging.c | 846 | ||||
-rw-r--r-- | main/bridging_after.c | 640 | ||||
-rw-r--r-- | main/bridging_basic.c | 12 | ||||
-rw-r--r-- | main/bridging_channel.c | 717 | ||||
-rw-r--r-- | main/features.c | 17 |
5 files changed, 1127 insertions, 1105 deletions
diff --git a/main/bridging.c b/main/bridging.c index dda073236..1e1e6c763 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -38,9 +38,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/linkedlists.h" #include "asterisk/bridging.h" +#include "asterisk/bridging_internal.h" +#include "asterisk/bridging_channel_internal.h" #include "asterisk/bridging_basic.h" #include "asterisk/bridging_technology.h" #include "asterisk/bridging_channel.h" +#include "asterisk/bridging_after.h" #include "asterisk/stasis_bridging.h" #include "asterisk/stasis_channels.h" #include "asterisk/app.h" @@ -61,8 +64,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/parking.h" #include "asterisk/core_local.h" #include "asterisk/core_unreal.h" -#include "asterisk/bridging_internal.h" -#include "asterisk/bridging_channel_internal.h" /*! All bridges container. */ static struct ao2_container *bridges; @@ -268,7 +269,7 @@ void bridge_dissolve(struct ast_bridge *bridge) /* BUGBUG need a cause code on the bridge for the later ejected channels. */ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } /* Must defer dissolving bridge because it is already locked. */ @@ -1343,24 +1344,6 @@ static void set_bridge_peer_vars(struct ast_bridge *bridge) } } -/*! - * \internal - * \brief Notify the bridge that it has been reconfigured. - * \since 12.0.0 - * - * \param bridge Reconfigured bridge. - * \param colp_update Whether to perform COLP updates. - * - * \details - * After a series of bridge_channel_push and - * bridge_channel_pull calls, you need to call this function - * to cause the bridge to complete restructuring for the change - * in the channel makeup of the bridge. - * - * \note On entry, the bridge is already locked. - * - * \return Nothing - */ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) { if (!bridge->reconfigured) { @@ -1387,726 +1370,17 @@ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) } } -/*! - * \internal - * \brief Close a pipe. - * \since 12.0.0 - * - * \param my_pipe What to close. - * - * \return Nothing - */ -static void pipe_close(int *my_pipe) -{ - if (my_pipe[0] > -1) { - close(my_pipe[0]); - my_pipe[0] = -1; - } - if (my_pipe[1] > -1) { - close(my_pipe[1]); - my_pipe[1] = -1; - } -} - -/*! - * \internal - * \brief Initialize a pipe as non-blocking. - * \since 12.0.0 - * - * \param my_pipe What to initialize. - * - * \retval 0 on success. - * \retval -1 on error. - */ -static int pipe_init_nonblock(int *my_pipe) -{ - int flags; - - my_pipe[0] = -1; - my_pipe[1] = -1; - if (pipe(my_pipe)) { - ast_log(LOG_WARNING, "Can't create pipe! Try increasing max file descriptors with ulimit -n\n"); - return -1; - } - flags = fcntl(my_pipe[0], F_GETFL); - if (fcntl(my_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) { - ast_log(LOG_WARNING, "Unable to set read pipe nonblocking! (%d: %s)\n", - errno, strerror(errno)); - return -1; - } - flags = fcntl(my_pipe[1], F_GETFL); - if (fcntl(my_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) { - ast_log(LOG_WARNING, "Unable to set write pipe nonblocking! (%d: %s)\n", - errno, strerror(errno)); - return -1; - } - return 0; -} - -/* Destroy elements of the bridge channel structure and the bridge channel structure itself */ -static void bridge_channel_destroy(void *obj) -{ - struct ast_bridge_channel *bridge_channel = obj; - struct ast_frame *fr; - - if (bridge_channel->callid) { - bridge_channel->callid = ast_callid_unref(bridge_channel->callid); - } - - if (bridge_channel->bridge) { - ao2_ref(bridge_channel->bridge, -1); - bridge_channel->bridge = NULL; - } - - /* Flush any unhandled wr_queue frames. */ - while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) { - ast_frfree(fr); - } - pipe_close(bridge_channel->alert_pipe); - - ast_cond_destroy(&bridge_channel->cond); -} - -static struct ast_bridge_channel *bridge_channel_alloc(struct ast_bridge *bridge) +struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan) { struct ast_bridge_channel *bridge_channel; - bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy); - if (!bridge_channel) { - return NULL; - } - ast_cond_init(&bridge_channel->cond, NULL); - if (pipe_init_nonblock(bridge_channel->alert_pipe)) { - ao2_ref(bridge_channel, -1); - return NULL; - } - if (bridge) { - bridge_channel->bridge = bridge; - ao2_ref(bridge_channel->bridge, +1); - } - - return bridge_channel; -} - -struct after_bridge_cb_node { - /*! Next list node. */ - AST_LIST_ENTRY(after_bridge_cb_node) list; - /*! Desired callback function. */ - ast_after_bridge_cb callback; - /*! After bridge callback will not be called and destroy any resources data may contain. */ - ast_after_bridge_cb_failed failed; - /*! Extra data to pass to the callback. */ - void *data; - /*! Reason the after bridge callback failed. */ - enum ast_after_bridge_cb_reason reason; -}; - -struct after_bridge_cb_ds { - /*! After bridge callbacks container. */ - AST_LIST_HEAD(, after_bridge_cb_node) callbacks; -}; - -/*! - * \internal - * \brief Indicate after bridge callback failed. - * \since 12.0.0 - * - * \param node After bridge callback node. - * - * \return Nothing - */ -static void after_bridge_cb_failed(struct after_bridge_cb_node *node) -{ - if (node->failed) { - node->failed(node->reason, node->data); - node->failed = NULL; - } -} - -/*! - * \internal - * \brief Run discarding any after bridge callbacks. - * \since 12.0.0 - * - * \param after_bridge After bridge callback container process. - * \param reason Why are we doing this. - * - * \return Nothing - */ -static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_after_bridge_cb_reason reason) -{ - struct after_bridge_cb_node *node; - - for (;;) { - AST_LIST_LOCK(&after_bridge->callbacks); - node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); - AST_LIST_UNLOCK(&after_bridge->callbacks); - if (!node) { - break; - } - if (!node->reason) { - node->reason = reason; - } - after_bridge_cb_failed(node); - ast_free(node); - } -} - -/*! - * \internal - * \brief Destroy the after bridge callback datastore. - * \since 12.0.0 - * - * \param data After bridge callback data to destroy. - * - * \return Nothing - */ -static void after_bridge_cb_destroy(void *data) -{ - struct after_bridge_cb_ds *after_bridge = data; - - after_bridge_cb_run_discard(after_bridge, AST_AFTER_BRIDGE_CB_REASON_DESTROY); - - AST_LIST_HEAD_DESTROY(&after_bridge->callbacks); - ast_free(after_bridge); -} - -/*! - * \internal - * \brief Fixup the after bridge callback datastore. - * \since 12.0.0 - * - * \param data After bridge callback data to fixup. - * \param old_chan The datastore is moving from this channel. - * \param new_chan The datastore is moving to this channel. - * - * \return Nothing - */ -static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - ast_after_bridge_callback_discard(new_chan, AST_AFTER_BRIDGE_CB_REASON_MASQUERADE); -} - -static const struct ast_datastore_info after_bridge_cb_info = { - .type = "after-bridge-cb", - .destroy = after_bridge_cb_destroy, - .chan_fixup = after_bridge_cb_fixup, -}; - -/*! - * \internal - * \brief Setup/create an after bridge callback datastore container. - * \since 12.0.0 - * - * \param chan Channel to setup/create the after bridge callback container on. - * - * \retval after_bridge datastore container on success. - * \retval NULL on error. - */ -static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - struct after_bridge_cb_ds *after_bridge; - SCOPED_CHANNELLOCK(lock, chan); - - datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); - if (datastore) { - return datastore->data; - } - - /* Create a new datastore. */ - datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL); - if (!datastore) { - return NULL; - } - after_bridge = ast_calloc(1, sizeof(*after_bridge)); - if (!after_bridge) { - ast_datastore_free(datastore); - return NULL; - } - AST_LIST_HEAD_INIT(&after_bridge->callbacks); - datastore->data = after_bridge; - ast_channel_datastore_add(chan, datastore); - - return datastore->data; -} - -/*! - * \internal - * \brief Find an after bridge callback datastore container. - * \since 12.0.0 - * - * \param chan Channel to find the after bridge callback container on. - * - * \retval after_bridge datastore container on success. - * \retval NULL on error. - */ -static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - SCOPED_CHANNELLOCK(lock, chan); - - datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); - if (!datastore) { - return NULL; - } - return datastore->data; -} - -/*! - * \internal - * \brief Run any after bridge callback. - * \since 12.0.0 - * - * \param chan Channel to run after bridge callback. - * - * \return Nothing - */ -static void after_bridge_callback_run(struct ast_channel *chan) -{ - struct after_bridge_cb_ds *after_bridge; - struct after_bridge_cb_node *node; - - after_bridge = after_bridge_cb_find(chan); - if (!after_bridge) { - return; - } - - for (;;) { - AST_LIST_LOCK(&after_bridge->callbacks); - node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); - AST_LIST_UNLOCK(&after_bridge->callbacks); - if (!node) { + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (bridge_channel->chan == chan) { break; } - if (node->reason) { - after_bridge_cb_failed(node); - } else { - node->failed = NULL; - node->callback(chan, node->data); - } - ast_free(node); } -} -/*! - * \internal - * \brief Run discarding any after bridge callbacks. - * \since 12.0.0 - * - * \param chan Channel to run after bridge callback. - * - * \return Nothing - */ -static void after_bridge_callback_run_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) -{ - struct after_bridge_cb_ds *after_bridge; - - after_bridge = after_bridge_cb_find(chan); - if (!after_bridge) { - return; - } - - after_bridge_cb_run_discard(after_bridge, reason); -} - -void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_bridge_cb_reason reason) -{ - struct after_bridge_cb_ds *after_bridge; - struct after_bridge_cb_node *node; - - after_bridge = after_bridge_cb_find(chan); - if (!after_bridge) { - return; - } - - AST_LIST_LOCK(&after_bridge->callbacks); - node = AST_LIST_LAST(&after_bridge->callbacks); - if (node && !node->reason) { - node->reason = reason; - } - AST_LIST_UNLOCK(&after_bridge->callbacks); -} - -int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data) -{ - struct after_bridge_cb_ds *after_bridge; - struct after_bridge_cb_node *new_node; - struct after_bridge_cb_node *last_node; - - /* Sanity checks. */ - ast_assert(chan != NULL); - if (!chan || !callback) { - return -1; - } - - after_bridge = after_bridge_cb_setup(chan); - if (!after_bridge) { - return -1; - } - - /* Create a new callback node. */ - new_node = ast_calloc(1, sizeof(*new_node)); - if (!new_node) { - return -1; - } - new_node->callback = callback; - new_node->failed = failed; - new_node->data = data; - - /* Put it in the container disabling any previously active one. */ - AST_LIST_LOCK(&after_bridge->callbacks); - last_node = AST_LIST_LAST(&after_bridge->callbacks); - if (last_node && !last_node->reason) { - last_node->reason = AST_AFTER_BRIDGE_CB_REASON_REPLACED; - } - AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list); - AST_LIST_UNLOCK(&after_bridge->callbacks); - return 0; -} - -const char *reason_strings[] = { - [AST_AFTER_BRIDGE_CB_REASON_DESTROY] = "Channel destroyed (hungup)", - [AST_AFTER_BRIDGE_CB_REASON_REPLACED] = "Callback was replaced", - [AST_AFTER_BRIDGE_CB_REASON_MASQUERADE] = "Channel masqueraded", - [AST_AFTER_BRIDGE_CB_REASON_DEPART] = "Channel was departed from bridge", - [AST_AFTER_BRIDGE_CB_REASON_REMOVED] = "Callback was removed", -}; - -const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason) -{ - if (reason < AST_AFTER_BRIDGE_CB_REASON_DESTROY - || AST_AFTER_BRIDGE_CB_REASON_REMOVED < reason - || !reason_strings[reason]) { - return "Unknown"; - } - - return reason_strings[reason]; -} - -struct after_bridge_goto_ds { - /*! Goto string that can be parsed by ast_parseable_goto(). */ - const char *parseable_goto; - /*! Specific goto context or default context for parseable_goto. */ - const char *context; - /*! Specific goto exten or default exten for parseable_goto. */ - const char *exten; - /*! Specific goto priority or default priority for parseable_goto. */ - int priority; - /*! TRUE if the peer should run the h exten. */ - unsigned int run_h_exten:1; - /*! Specific goto location */ - unsigned int specific:1; -}; - -/*! - * \internal - * \brief Destroy the after bridge goto datastore. - * \since 12.0.0 - * - * \param data After bridge goto data to destroy. - * - * \return Nothing - */ -static void after_bridge_goto_destroy(void *data) -{ - struct after_bridge_goto_ds *after_bridge = data; - - ast_free((char *) after_bridge->parseable_goto); - ast_free((char *) after_bridge->context); - ast_free((char *) after_bridge->exten); -} - -/*! - * \internal - * \brief Fixup the after bridge goto datastore. - * \since 12.0.0 - * - * \param data After bridge goto data to fixup. - * \param old_chan The datastore is moving from this channel. - * \param new_chan The datastore is moving to this channel. - * - * \return Nothing - */ -static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - /* There can be only one. Discard any already on the new channel. */ - ast_after_bridge_goto_discard(new_chan); -} - -static const struct ast_datastore_info after_bridge_goto_info = { - .type = "after-bridge-goto", - .destroy = after_bridge_goto_destroy, - .chan_fixup = after_bridge_goto_fixup, -}; - -/*! - * \internal - * \brief Remove channel goto location after the bridge and return it. - * \since 12.0.0 - * - * \param chan Channel to remove after bridge goto location. - * - * \retval datastore on success. - * \retval NULL on error or not found. - */ -static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - - ast_channel_lock(chan); - datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); - if (datastore && ast_channel_datastore_remove(chan, datastore)) { - datastore = NULL; - } - ast_channel_unlock(chan); - - return datastore; -} - -void ast_after_bridge_goto_discard(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - - datastore = after_bridge_goto_remove(chan); - if (datastore) { - ast_datastore_free(datastore); - } -} - -void ast_after_bridge_goto_read(struct ast_channel *chan, char *buffer, size_t buf_size) -{ - struct ast_datastore *datastore; - struct after_bridge_goto_ds *after_bridge; - char *current_pos = buffer; - size_t remaining_size = buf_size; - - SCOPED_CHANNELLOCK(lock, chan); - - datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); - if (!datastore) { - buffer[0] = '\0'; - return; - } - - after_bridge = datastore->data; - - if (after_bridge->parseable_goto) { - snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto); - return; - } - - if (!ast_strlen_zero(after_bridge->context)) { - snprintf(current_pos, remaining_size, "%s,", after_bridge->context); - remaining_size = remaining_size - strlen(current_pos); - current_pos += strlen(current_pos); - } - - if (after_bridge->run_h_exten) { - snprintf(current_pos, remaining_size, "h,"); - remaining_size = remaining_size - strlen(current_pos); - current_pos += strlen(current_pos); - } else if (!ast_strlen_zero(after_bridge->exten)) { - snprintf(current_pos, remaining_size, "%s,", after_bridge->exten); - remaining_size = remaining_size - strlen(current_pos); - current_pos += strlen(current_pos); - } - - snprintf(current_pos, remaining_size, "%d", after_bridge->priority); -} - -int ast_after_bridge_goto_setup(struct ast_channel *chan) -{ - struct ast_datastore *datastore; - struct after_bridge_goto_ds *after_bridge; - int goto_failed = -1; - - /* Determine if we are going to setup a dialplan location and where. */ - if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { - /* An async goto has already setup a location. */ - ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO); - if (!ast_check_hangup(chan)) { - goto_failed = 0; - } - return goto_failed; - } - - /* Get after bridge goto datastore. */ - datastore = after_bridge_goto_remove(chan); - if (!datastore) { - return goto_failed; - } - - after_bridge = datastore->data; - if (after_bridge->run_h_exten) { - if (ast_exists_extension(chan, after_bridge->context, "h", 1, - S_COR(ast_channel_caller(chan)->id.number.valid, - ast_channel_caller(chan)->id.number.str, NULL))) { - ast_debug(1, "Running after bridge goto h exten %s,h,1\n", - ast_channel_context(chan)); - ast_pbx_h_exten_run(chan, after_bridge->context); - } - } else if (!ast_check_hangup(chan)) { - if (after_bridge->specific) { - goto_failed = ast_explicit_goto(chan, after_bridge->context, - after_bridge->exten, after_bridge->priority); - } else if (!ast_strlen_zero(after_bridge->parseable_goto)) { - char *context; - char *exten; - int priority; - - /* Option F(x) for Bridge(), Dial(), and Queue() */ - - /* Save current dialplan location in case of failure. */ - context = ast_strdupa(ast_channel_context(chan)); - exten = ast_strdupa(ast_channel_exten(chan)); - priority = ast_channel_priority(chan); - - /* Set current dialplan position to default dialplan position */ - ast_explicit_goto(chan, after_bridge->context, after_bridge->exten, - after_bridge->priority); - - /* Then perform the goto */ - goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto); - if (goto_failed) { - /* Restore original dialplan location. */ - ast_channel_context_set(chan, context); - ast_channel_exten_set(chan, exten); - ast_channel_priority_set(chan, priority); - } - } else { - /* Option F() for Bridge(), Dial(), and Queue() */ - goto_failed = ast_goto_if_exists(chan, after_bridge->context, - after_bridge->exten, after_bridge->priority + 1); - } - if (!goto_failed) { - if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) { - ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); - } - - ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n", - ast_channel_context(chan), - ast_channel_exten(chan), - ast_channel_priority(chan)); - } - } - - /* Discard after bridge goto datastore. */ - ast_datastore_free(datastore); - - return goto_failed; -} - -void ast_after_bridge_goto_run(struct ast_channel *chan) -{ - int goto_failed; - - goto_failed = ast_after_bridge_goto_setup(chan); - if (goto_failed || ast_pbx_run(chan)) { - ast_hangup(chan); - } -} - -/*! - * \internal - * \brief Set after bridge goto location of channel. - * \since 12.0.0 - * - * \param chan Channel to setup after bridge goto location. - * \param run_h_exten TRUE if the h exten should be run. - * \param specific TRUE if the context/exten/priority is exactly specified. - * \param context Context to goto after bridge. - * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten) - * \param priority Priority to goto after bridge. - * \param parseable_goto User specified goto string. (Could be NULL) - * - * \details Add a channel datastore to setup the goto location - * when the channel leaves the bridge and run a PBX from there. - * - * If run_h_exten then execute the h exten found in the given context. - * Else if specific then goto the given context/exten/priority. - * Else if parseable_goto then use the given context/exten/priority - * as the relative position for the parseable_goto. - * Else goto the given context/exten/priority+1. - * - * \return Nothing - */ -static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto) -{ - struct ast_datastore *datastore; - struct after_bridge_goto_ds *after_bridge; - - /* Sanity checks. */ - ast_assert(chan != NULL); - if (!chan) { - return; - } - if (run_h_exten) { - ast_assert(run_h_exten && context); - if (!context) { - return; - } - } else { - ast_assert(context && exten && 0 < priority); - if (!context || !exten || priority < 1) { - return; - } - } - - /* Create a new datastore. */ - datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL); - if (!datastore) { - return; - } - after_bridge = ast_calloc(1, sizeof(*after_bridge)); - if (!after_bridge) { - ast_datastore_free(datastore); - return; - } - - /* Initialize it. */ - after_bridge->parseable_goto = ast_strdup(parseable_goto); - after_bridge->context = ast_strdup(context); - after_bridge->exten = ast_strdup(exten); - after_bridge->priority = priority; - after_bridge->run_h_exten = run_h_exten ? 1 : 0; - after_bridge->specific = specific ? 1 : 0; - datastore->data = after_bridge; - if ((parseable_goto && !after_bridge->parseable_goto) - || (context && !after_bridge->context) - || (exten && !after_bridge->exten)) { - ast_datastore_free(datastore); - return; - } - - /* Put it on the channel replacing any existing one. */ - ast_channel_lock(chan); - ast_after_bridge_goto_discard(chan); - ast_channel_datastore_add(chan, datastore); - ast_channel_unlock(chan); -} - -void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority) -{ - __after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL); -} - -void ast_after_bridge_set_h(struct ast_channel *chan, const char *context) -{ - __after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL); -} - -void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto) -{ - char *p_goto; - - if (!ast_strlen_zero(parseable_goto)) { - p_goto = ast_strdupa(parseable_goto); - ast_replace_subargument_delimiter(p_goto); - } else { - p_goto = NULL; - } - __after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto); + return bridge_channel; } void ast_bridge_notify_masquerade(struct ast_channel *chan) @@ -2146,7 +1420,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan) * Need to update the features parameter doxygen when this * change is made to be like ast_bridge_impart(). */ -enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, +enum bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, @@ -2154,21 +1428,21 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, int pass_reference) { struct ast_bridge_channel *bridge_channel; - enum ast_bridge_channel_state state; + enum bridge_channel_state state; - bridge_channel = bridge_channel_alloc(bridge); + bridge_channel = bridge_channel_internal_alloc(bridge); if (pass_reference) { ao2_ref(bridge, -1); } if (!bridge_channel) { - state = AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; + state = BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; goto join_exit; } /* BUGBUG features cannot be NULL when passed in. When it is changed to allocated we can do like ast_bridge_impart() and allocate one. */ ast_assert(features != NULL); if (!features) { ao2_ref(bridge_channel, -1); - state = AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; + state = BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE; goto join_exit; } if (tech_args) { @@ -2184,7 +1458,7 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, bridge_channel->swap = swap; bridge_channel->features = features; - bridge_channel_join(bridge_channel); + bridge_channel_internal_join(bridge_channel); state = bridge_channel->state; /* Cleanup all the data in the bridge channel after it leaves the bridge. */ @@ -2199,9 +1473,9 @@ enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, join_exit:; /* BUGBUG this is going to cause problems for DTMF atxfer attended bridge between B & C. Maybe an ast_bridge_join_internal() that does not do the after bridge goto for this case. */ - after_bridge_callback_run(chan); + ast_bridge_run_after_callback(chan); if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) - && !ast_after_bridge_goto_setup(chan)) { + && !ast_bridge_setup_after_goto(chan)) { /* Claim the after bridge goto is an async goto destination. */ ast_channel_lock(chan); ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO); @@ -2219,15 +1493,15 @@ static void *bridge_channel_depart_thread(void *data) ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_join(bridge_channel); + bridge_channel_internal_join(bridge_channel); /* cleanup */ bridge_channel->swap = NULL; ast_bridge_features_destroy(bridge_channel->features); bridge_channel->features = NULL; - after_bridge_callback_run_discard(bridge_channel->chan, AST_AFTER_BRIDGE_CB_REASON_DEPART); - ast_after_bridge_goto_discard(bridge_channel->chan); + ast_bridge_discard_after_callback(bridge_channel->chan, AST_BRIDGE_AFTER_CB_REASON_DEPART); + ast_bridge_discard_after_goto(bridge_channel->chan); return NULL; } @@ -2242,7 +1516,7 @@ static void *bridge_channel_ind_thread(void *data) ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_join(bridge_channel); + bridge_channel_internal_join(bridge_channel); chan = bridge_channel->chan; /* cleanup */ @@ -2256,8 +1530,8 @@ static void *bridge_channel_ind_thread(void *data) ao2_ref(bridge_channel, -1); - after_bridge_callback_run(chan); - ast_after_bridge_goto_run(chan); + ast_bridge_run_after_callback(chan); + ast_bridge_run_after_goto(chan); return NULL; } @@ -2275,7 +1549,7 @@ int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struc } /* Try to allocate a structure for the bridge channel */ - bridge_channel = bridge_channel_alloc(bridge); + bridge_channel = bridge_channel_internal_alloc(bridge); if (!bridge_channel) { ast_bridge_features_destroy(features); return -1; @@ -2350,7 +1624,7 @@ int ast_bridge_depart(struct ast_channel *chan) * channel thread. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); /* Wait for the depart thread to die */ ast_debug(1, "Waiting for %p(%s) bridge thread to die.\n", @@ -2378,7 +1652,7 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); ast_bridge_unlock(bridge); @@ -2425,10 +1699,10 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg * Move channels from src_bridge over to dst_bridge. * * We must use AST_LIST_TRAVERSE_SAFE_BEGIN() because - * bridge_channel_pull() alters the list we are traversing. + * bridge_channel_internal_pull() alters the list we are traversing. */ AST_LIST_TRAVERSE_SAFE_BEGIN(&src_bridge->channels, bridge_channel, entry) { - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* * The channel is already leaving let it leave normally because * pulling it may delete hooks that should run for this channel. @@ -2443,13 +1717,13 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg if (kick_me) { for (idx = 0; idx < num_kick; ++idx) { if (bridge_channel == kick_me[idx]) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); break; } } } - bridge_channel_pull(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + bridge_channel_internal_pull(bridge_channel); + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* * The channel died as a result of being pulled or it was * kicked. Leave it pointing to the original bridge. @@ -2460,8 +1734,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg /* Point to new bridge.*/ bridge_channel_change_bridge(bridge_channel, dst_bridge); - if (bridge_channel_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + if (bridge_channel_internal_push(bridge_channel)) { + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } } AST_LIST_TRAVERSE_SAFE_END; @@ -2474,9 +1748,9 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg for (idx = 0; idx < num_kick; ++idx) { bridge_channel = kick_me[idx]; ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { - ast_bridge_channel_leave_bridge_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); - bridge_channel_pull(bridge_channel); + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { + ast_bridge_channel_leave_bridge_nolock(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + bridge_channel_internal_pull(bridge_channel); } ast_bridge_channel_unlock(bridge_channel); } @@ -2689,8 +1963,8 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri orig_bridge = bridge_channel->bridge; was_in_bridge = bridge_channel->in_bridge; - bridge_channel_pull(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + bridge_channel_internal_pull(bridge_channel); + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* * The channel died as a result of being pulled. Leave it * pointing to the original bridge. @@ -2703,17 +1977,17 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri ao2_ref(orig_bridge, +1);/* Keep a ref in case the push fails. */ bridge_channel_change_bridge(bridge_channel, dst_bridge); - if (bridge_channel_push(bridge_channel)) { + if (bridge_channel_internal_push(bridge_channel)) { /* Try to put the channel back into the original bridge. */ if (attempt_recovery && was_in_bridge) { /* Point back to original bridge. */ bridge_channel_change_bridge(bridge_channel, orig_bridge); - if (bridge_channel_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + if (bridge_channel_internal_push(bridge_channel)) { + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } } else { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } res = -1; } @@ -2767,7 +2041,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid); return -1; } - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel leaving bridge.\n", ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid); return -1; @@ -2789,7 +2063,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge * ast_channel_name(swap)); return -1; } - if (bridge_channel_swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel_swap->state != BRIDGE_CHANNEL_STATE_WAIT) { ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, swap channel %s leaving bridge.\n", ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid, ast_channel_name(swap)); @@ -2894,12 +2168,6 @@ static int bridge_allows_optimization(struct ast_bridge *bridge) || ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)); } -static int bridge_channel_allows_optimization(struct ast_bridge_channel *bridge_channel) -{ - return bridge_channel->in_bridge - && AST_LIST_EMPTY(&bridge_channel->wr_queue); -} - /*! * \internal * \brief Lock the unreal channel stack for chan and prequalify it. @@ -2929,13 +2197,13 @@ static struct ast_bridge *optimize_lock_chan_stack(struct ast_channel *chan) return NULL; } bridge = bridge_channel->bridge; - if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_SIMPLE - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT + if (bridge_channel->activity != BRIDGE_CHANNEL_THREAD_SIMPLE + || bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT || ast_bridge_trylock(bridge)) { ast_bridge_channel_unlock(bridge_channel); return NULL; } - if (!bridge_channel_allows_optimization(bridge_channel) || + if (!bridge_channel_internal_allows_optimization(bridge_channel) || !bridge_allows_optimization(bridge)) { ast_bridge_unlock(bridge); ast_bridge_channel_unlock(bridge_channel); @@ -2977,15 +2245,15 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer) return NULL; } bridge = bridge_channel->bridge; - if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_IDLE - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT + if (bridge_channel->activity != BRIDGE_CHANNEL_THREAD_IDLE + || bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT || ast_bridge_trylock(bridge)) { ast_bridge_channel_unlock(bridge_channel); ast_channel_unlock(peer); return NULL; } if (!bridge_allows_optimization(bridge) || - !bridge_channel_allows_optimization(bridge_channel)) { + !bridge_channel_internal_allows_optimization(bridge_channel)) { ast_bridge_unlock(bridge); ast_bridge_channel_unlock(bridge_channel); ast_channel_unlock(peer); @@ -3099,7 +2367,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge, } other = ast_bridge_channel_peer(src_bridge_channel); - if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (other && other->state == BRIDGE_CHANNEL_STATE_WAIT) { ast_verb(3, "Move-swap optimizing %s <-- %s.\n", ast_channel_name(dst_bridge_channel->chan), ast_channel_name(other->chan)); @@ -3111,7 +2379,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge, } other->swap = dst_bridge_channel->chan; if (!bridge_do_move(dst_bridge, other, 1, 1)) { - ast_bridge_channel_leave_bridge(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(src_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); res = -1; if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) { pvt->callbacks->optimization_finished(pvt); @@ -3343,7 +2611,7 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - bridge_channel_suspend_nolock(bridge_channel); + bridge_channel_internal_suspend_nolock(bridge_channel); ast_bridge_unlock(bridge); @@ -3362,7 +2630,7 @@ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - bridge_channel_unsuspend_nolock(bridge_channel); + bridge_channel_internal_unsuspend_nolock(bridge_channel); ast_bridge_unlock(bridge); @@ -4686,7 +3954,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, goto publish; } - if (bridge_channel_queue_blind_transfer(transferee, exten, context, + if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context, new_channel_cb, user_data)) { transfer_result = AST_BRIDGE_TRANSFER_FAIL; goto publish; @@ -4722,7 +3990,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge bridged_to_source = ast_bridge_channel_peer(source_bridge_channel); if (bridged_to_source - && bridged_to_source->state == AST_BRIDGE_CHANNEL_STATE_WAIT + && bridged_to_source->state == BRIDGE_CHANNEL_STATE_WAIT && !ast_test_flag(&bridged_to_source->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) { bridged_to_source->swap = swap_channel; @@ -4730,7 +3998,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge return AST_BRIDGE_TRANSFER_FAIL; } /* Must kick the source channel out of its bridge. */ - ast_bridge_channel_leave_bridge(source_bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(source_bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); return AST_BRIDGE_TRANSFER_SUCCESS; } else { return AST_BRIDGE_TRANSFER_INVALID; @@ -4953,7 +4221,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra } app = ast_strdupa(ast_channel_appl(chan_unbridged)); - if (bridge_channel_queue_attended_transfer(transferee, chan_unbridged)) { + if (bridge_channel_internal_queue_attended_transfer(transferee, chan_unbridged)) { res = AST_BRIDGE_TRANSFER_FAIL; goto end; } diff --git a/main/bridging_after.c b/main/bridging_after.c new file mode 100644 index 000000000..8ab4bc842 --- /dev/null +++ b/main/bridging_after.c @@ -0,0 +1,640 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2007 - 2009, Digium, Inc. + * + * Richard Mudgett <rmudgett@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief After Bridge Execution API + * + * \author Richard Mudgett <rmudgett@digium.com> + * + * See Also: + * \arg \ref AstCREDITS + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/bridging_after.h" + +struct after_bridge_cb_node { + /*! Next list node. */ + AST_LIST_ENTRY(after_bridge_cb_node) list; + /*! Desired callback function. */ + ast_bridge_after_cb callback; + /*! After bridge callback will not be called and destroy any resources data may contain. */ + ast_bridge_after_cb_failed failed; + /*! Extra data to pass to the callback. */ + void *data; + /*! Reason the after bridge callback failed. */ + enum ast_bridge_after_cb_reason reason; +}; + +struct after_bridge_cb_ds { + /*! After bridge callbacks container. */ + AST_LIST_HEAD(, after_bridge_cb_node) callbacks; +}; + +/*! + * \internal + * \brief Indicate after bridge callback failed. + * \since 12.0.0 + * + * \param node After bridge callback node. + * + * \return Nothing + */ +static void after_bridge_cb_failed(struct after_bridge_cb_node *node) +{ + if (node->failed) { + node->failed(node->reason, node->data); + node->failed = NULL; + } +} + +/*! + * \internal + * \brief Run discarding any after bridge callbacks. + * \since 12.0.0 + * + * \param after_bridge After bridge callback container process. + * \param reason Why are we doing this. + * + * \return Nothing + */ +static void after_bridge_cb_run_discard(struct after_bridge_cb_ds *after_bridge, enum ast_bridge_after_cb_reason reason) +{ + struct after_bridge_cb_node *node; + + for (;;) { + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + if (!node) { + break; + } + if (!node->reason) { + node->reason = reason; + } + after_bridge_cb_failed(node); + ast_free(node); + } +} + +/*! + * \internal + * \brief Destroy the after bridge callback datastore. + * \since 12.0.0 + * + * \param data After bridge callback data to destroy. + * + * \return Nothing + */ +static void after_bridge_cb_destroy(void *data) +{ + struct after_bridge_cb_ds *after_bridge = data; + + after_bridge_cb_run_discard(after_bridge, AST_BRIDGE_AFTER_CB_REASON_DESTROY); + + AST_LIST_HEAD_DESTROY(&after_bridge->callbacks); + ast_free(after_bridge); +} + +static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan); + +/*! + * \internal + * \brief Fixup the after bridge callback datastore. + * \since 12.0.0 + * + * \param data After bridge callback data to fixup. + * \param old_chan The datastore is moving from this channel. + * \param new_chan The datastore is moving to this channel. + * + * \return Nothing + */ +static void after_bridge_cb_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *node; + + after_bridge = after_bridge_cb_find(new_chan); + if (!after_bridge) { + return; + } + + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_LAST(&after_bridge->callbacks); + if (node && !node->reason) { + node->reason = AST_BRIDGE_AFTER_CB_REASON_MASQUERADE; + } + AST_LIST_UNLOCK(&after_bridge->callbacks); +} + +static const struct ast_datastore_info after_bridge_cb_info = { + .type = "after-bridge-cb", + .destroy = after_bridge_cb_destroy, + .chan_fixup = after_bridge_cb_fixup, +}; + +/*! + * \internal + * \brief Find an after bridge callback datastore container. + * \since 12.0.0 + * + * \param chan Channel to find the after bridge callback container on. + * + * \retval after_bridge datastore container on success. + * \retval NULL on error. + */ +static struct after_bridge_cb_ds *after_bridge_cb_find(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + SCOPED_CHANNELLOCK(lock, chan); + + datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); + if (!datastore) { + return NULL; + } + return datastore->data; +} + +/*! + * \internal + * \brief Setup/create an after bridge callback datastore container. + * \since 12.0.0 + * + * \param chan Channel to setup/create the after bridge callback container on. + * + * \retval after_bridge datastore container on success. + * \retval NULL on error. + */ +static struct after_bridge_cb_ds *after_bridge_cb_setup(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + struct after_bridge_cb_ds *after_bridge; + SCOPED_CHANNELLOCK(lock, chan); + + datastore = ast_channel_datastore_find(chan, &after_bridge_cb_info, NULL); + if (datastore) { + return datastore->data; + } + + /* Create a new datastore. */ + datastore = ast_datastore_alloc(&after_bridge_cb_info, NULL); + if (!datastore) { + return NULL; + } + after_bridge = ast_calloc(1, sizeof(*after_bridge)); + if (!after_bridge) { + ast_datastore_free(datastore); + return NULL; + } + AST_LIST_HEAD_INIT(&after_bridge->callbacks); + datastore->data = after_bridge; + ast_channel_datastore_add(chan, datastore); + + return datastore->data; +} + +void ast_bridge_run_after_callback(struct ast_channel *chan) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *node; + + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { + return; + } + + for (;;) { + AST_LIST_LOCK(&after_bridge->callbacks); + node = AST_LIST_REMOVE_HEAD(&after_bridge->callbacks, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + if (!node) { + break; + } + if (node->reason) { + after_bridge_cb_failed(node); + } else { + node->failed = NULL; + node->callback(chan, node->data); + } + ast_free(node); + } +} + +void ast_bridge_discard_after_callback(struct ast_channel *chan, enum ast_bridge_after_cb_reason reason) +{ + struct after_bridge_cb_ds *after_bridge; + + after_bridge = after_bridge_cb_find(chan); + if (!after_bridge) { + return; + } + + after_bridge_cb_run_discard(after_bridge, reason); +} + +int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb callback, ast_bridge_after_cb_failed failed, void *data) +{ + struct after_bridge_cb_ds *after_bridge; + struct after_bridge_cb_node *new_node; + struct after_bridge_cb_node *last_node; + + /* Sanity checks. */ + ast_assert(chan != NULL); + if (!chan || !callback) { + return -1; + } + + after_bridge = after_bridge_cb_setup(chan); + if (!after_bridge) { + return -1; + } + + /* Create a new callback node. */ + new_node = ast_calloc(1, sizeof(*new_node)); + if (!new_node) { + return -1; + } + new_node->callback = callback; + new_node->failed = failed; + new_node->data = data; + + /* Put it in the container disabling any previously active one. */ + AST_LIST_LOCK(&after_bridge->callbacks); + last_node = AST_LIST_LAST(&after_bridge->callbacks); + if (last_node && !last_node->reason) { + last_node->reason = AST_BRIDGE_AFTER_CB_REASON_REPLACED; + } + AST_LIST_INSERT_TAIL(&after_bridge->callbacks, new_node, list); + AST_LIST_UNLOCK(&after_bridge->callbacks); + return 0; +} + +const char *reason_strings[] = { + [AST_BRIDGE_AFTER_CB_REASON_DESTROY] = "Channel destroyed (hungup)", + [AST_BRIDGE_AFTER_CB_REASON_REPLACED] = "Callback was replaced", + [AST_BRIDGE_AFTER_CB_REASON_MASQUERADE] = "Channel masqueraded", + [AST_BRIDGE_AFTER_CB_REASON_DEPART] = "Channel was departed from bridge", + [AST_BRIDGE_AFTER_CB_REASON_REMOVED] = "Callback was removed", +}; + +const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason) +{ + if (reason < AST_BRIDGE_AFTER_CB_REASON_DESTROY + || AST_BRIDGE_AFTER_CB_REASON_REMOVED < reason + || !reason_strings[reason]) { + return "Unknown"; + } + + return reason_strings[reason]; +} + +struct after_bridge_goto_ds { + /*! Goto string that can be parsed by ast_parseable_goto(). */ + const char *parseable_goto; + /*! Specific goto context or default context for parseable_goto. */ + const char *context; + /*! Specific goto exten or default exten for parseable_goto. */ + const char *exten; + /*! Specific goto priority or default priority for parseable_goto. */ + int priority; + /*! TRUE if the peer should run the h exten. */ + unsigned int run_h_exten:1; + /*! Specific goto location */ + unsigned int specific:1; +}; + +/*! + * \internal + * \brief Destroy the after bridge goto datastore. + * \since 12.0.0 + * + * \param data After bridge goto data to destroy. + * + * \return Nothing + */ +static void after_bridge_goto_destroy(void *data) +{ + struct after_bridge_goto_ds *after_bridge = data; + + ast_free((char *) after_bridge->parseable_goto); + ast_free((char *) after_bridge->context); + ast_free((char *) after_bridge->exten); +} + +/*! + * \internal + * \brief Fixup the after bridge goto datastore. + * \since 12.0.0 + * + * \param data After bridge goto data to fixup. + * \param old_chan The datastore is moving from this channel. + * \param new_chan The datastore is moving to this channel. + * + * \return Nothing + */ +static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +{ + /* There can be only one. Discard any already on the new channel. */ + ast_bridge_discard_after_goto(new_chan); +} + +static const struct ast_datastore_info after_bridge_goto_info = { + .type = "after-bridge-goto", + .destroy = after_bridge_goto_destroy, + .chan_fixup = after_bridge_goto_fixup, +}; + +/*! + * \internal + * \brief Remove channel goto location after the bridge and return it. + * \since 12.0.0 + * + * \param chan Channel to remove after bridge goto location. + * + * \retval datastore on success. + * \retval NULL on error or not found. + */ +static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); + if (datastore && ast_channel_datastore_remove(chan, datastore)) { + datastore = NULL; + } + ast_channel_unlock(chan); + + return datastore; +} + +void ast_bridge_discard_after_goto(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + + datastore = after_bridge_goto_remove(chan); + if (datastore) { + ast_datastore_free(datastore); + } +} + +void ast_bridge_read_after_goto(struct ast_channel *chan, char *buffer, size_t buf_size) +{ + struct ast_datastore *datastore; + struct after_bridge_goto_ds *after_bridge; + char *current_pos = buffer; + size_t remaining_size = buf_size; + + SCOPED_CHANNELLOCK(lock, chan); + + datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL); + if (!datastore) { + buffer[0] = '\0'; + return; + } + + after_bridge = datastore->data; + + if (after_bridge->parseable_goto) { + snprintf(buffer, buf_size, "%s", after_bridge->parseable_goto); + return; + } + + if (!ast_strlen_zero(after_bridge->context)) { + snprintf(current_pos, remaining_size, "%s,", after_bridge->context); + remaining_size = remaining_size - strlen(current_pos); + current_pos += strlen(current_pos); + } + + if (after_bridge->run_h_exten) { + snprintf(current_pos, remaining_size, "h,"); + remaining_size = remaining_size - strlen(current_pos); + current_pos += strlen(current_pos); + } else if (!ast_strlen_zero(after_bridge->exten)) { + snprintf(current_pos, remaining_size, "%s,", after_bridge->exten); + remaining_size = remaining_size - strlen(current_pos); + current_pos += strlen(current_pos); + } + + snprintf(current_pos, remaining_size, "%d", after_bridge->priority); +} + +int ast_bridge_setup_after_goto(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + struct after_bridge_goto_ds *after_bridge; + int goto_failed = -1; + + /* Determine if we are going to setup a dialplan location and where. */ + if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { + /* An async goto has already setup a location. */ + ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO); + if (!ast_check_hangup(chan)) { + goto_failed = 0; + } + return goto_failed; + } + + /* Get after bridge goto datastore. */ + datastore = after_bridge_goto_remove(chan); + if (!datastore) { + return goto_failed; + } + + after_bridge = datastore->data; + if (after_bridge->run_h_exten) { + if (ast_exists_extension(chan, after_bridge->context, "h", 1, + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, NULL))) { + ast_debug(1, "Running after bridge goto h exten %s,h,1\n", + ast_channel_context(chan)); + ast_pbx_h_exten_run(chan, after_bridge->context); + } + } else if (!ast_check_hangup(chan)) { + if (after_bridge->specific) { + goto_failed = ast_explicit_goto(chan, after_bridge->context, + after_bridge->exten, after_bridge->priority); + } else if (!ast_strlen_zero(after_bridge->parseable_goto)) { + char *context; + char *exten; + int priority; + + /* Option F(x) for Bridge(), Dial(), and Queue() */ + + /* Save current dialplan location in case of failure. */ + context = ast_strdupa(ast_channel_context(chan)); + exten = ast_strdupa(ast_channel_exten(chan)); + priority = ast_channel_priority(chan); + + /* Set current dialplan position to default dialplan position */ + ast_explicit_goto(chan, after_bridge->context, after_bridge->exten, + after_bridge->priority); + + /* Then perform the goto */ + goto_failed = ast_parseable_goto(chan, after_bridge->parseable_goto); + if (goto_failed) { + /* Restore original dialplan location. */ + ast_channel_context_set(chan, context); + ast_channel_exten_set(chan, exten); + ast_channel_priority_set(chan, priority); + } + } else { + /* Option F() for Bridge(), Dial(), and Queue() */ + goto_failed = ast_goto_if_exists(chan, after_bridge->context, + after_bridge->exten, after_bridge->priority + 1); + } + if (!goto_failed) { + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) { + ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); + } + + ast_debug(1, "Setup after bridge goto location to %s,%s,%d.\n", + ast_channel_context(chan), + ast_channel_exten(chan), + ast_channel_priority(chan)); + } + } + + /* Discard after bridge goto datastore. */ + ast_datastore_free(datastore); + + return goto_failed; +} + +void ast_bridge_run_after_goto(struct ast_channel *chan) +{ + int goto_failed; + + goto_failed = ast_bridge_setup_after_goto(chan); + if (goto_failed || ast_pbx_run(chan)) { + ast_hangup(chan); + } +} + +/*! + * \internal + * \brief Set after bridge goto location of channel. + * \since 12.0.0 + * + * \param chan Channel to setup after bridge goto location. + * \param run_h_exten TRUE if the h exten should be run. + * \param specific TRUE if the context/exten/priority is exactly specified. + * \param context Context to goto after bridge. + * \param exten Exten to goto after bridge. (Could be NULL if run_h_exten) + * \param priority Priority to goto after bridge. + * \param parseable_goto User specified goto string. (Could be NULL) + * + * \details Add a channel datastore to setup the goto location + * when the channel leaves the bridge and run a PBX from there. + * + * If run_h_exten then execute the h exten found in the given context. + * Else if specific then goto the given context/exten/priority. + * Else if parseable_goto then use the given context/exten/priority + * as the relative position for the parseable_goto. + * Else goto the given context/exten/priority+1. + * + * \return Nothing + */ +static void __after_bridge_set_goto(struct ast_channel *chan, int run_h_exten, int specific, const char *context, const char *exten, int priority, const char *parseable_goto) +{ + struct ast_datastore *datastore; + struct after_bridge_goto_ds *after_bridge; + + /* Sanity checks. */ + ast_assert(chan != NULL); + if (!chan) { + return; + } + if (run_h_exten) { + ast_assert(run_h_exten && context); + if (!context) { + return; + } + } else { + ast_assert(context && exten && 0 < priority); + if (!context || !exten || priority < 1) { + return; + } + } + + /* Create a new datastore. */ + datastore = ast_datastore_alloc(&after_bridge_goto_info, NULL); + if (!datastore) { + return; + } + after_bridge = ast_calloc(1, sizeof(*after_bridge)); + if (!after_bridge) { + ast_datastore_free(datastore); + return; + } + + /* Initialize it. */ + after_bridge->parseable_goto = ast_strdup(parseable_goto); + after_bridge->context = ast_strdup(context); + after_bridge->exten = ast_strdup(exten); + after_bridge->priority = priority; + after_bridge->run_h_exten = run_h_exten ? 1 : 0; + after_bridge->specific = specific ? 1 : 0; + datastore->data = after_bridge; + if ((parseable_goto && !after_bridge->parseable_goto) + || (context && !after_bridge->context) + || (exten && !after_bridge->exten)) { + ast_datastore_free(datastore); + return; + } + + /* Put it on the channel replacing any existing one. */ + ast_channel_lock(chan); + ast_bridge_discard_after_goto(chan); + ast_channel_datastore_add(chan, datastore); + ast_channel_unlock(chan); +} + +void ast_bridge_set_after_goto(struct ast_channel *chan, const char *context, const char *exten, int priority) +{ + __after_bridge_set_goto(chan, 0, 1, context, exten, priority, NULL); +} + +void ast_bridge_set_after_h(struct ast_channel *chan, const char *context) +{ + __after_bridge_set_goto(chan, 1, 0, context, NULL, 1, NULL); +} + +void ast_bridge_set_after_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto) +{ + char *p_goto; + + if (!ast_strlen_zero(parseable_goto)) { + p_goto = ast_strdupa(parseable_goto); + ast_replace_subargument_delimiter(p_goto); + } else { + p_goto = NULL; + } + __after_bridge_set_goto(chan, 0, 0, context, exten, priority, p_goto); +} diff --git a/main/bridging_basic.c b/main/bridging_basic.c index 8fcad7560..7f9cb71fd 100644 --- a/main/bridging_basic.c +++ b/main/bridging_basic.c @@ -36,13 +36,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/linkedlists.h" #include "asterisk/bridging.h" #include "asterisk/bridging_basic.h" +#include "asterisk/bridging_after.h" #include "asterisk/astobj2.h" #include "asterisk/features_config.h" #include "asterisk/pbx.h" #include "asterisk/file.h" #include "asterisk/app.h" #include "asterisk/bridging_internal.h" -#include "asterisk/bridging_channel_internal.h" #include "asterisk/dial.h" #include "asterisk/stasis_bridging.h" @@ -279,13 +279,13 @@ static int basic_hangup_hook(struct ast_bridge *bridge, struct ast_bridge_channe ast_bridge_channel_lock_bridge(bridge_channel); AST_LIST_TRAVERSE(&bridge->channels, iter, entry) { - if (iter != bridge_channel && iter->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (iter != bridge_channel && iter->state == BRIDGE_CHANNEL_STATE_WAIT) { ++bridge_count; } } if (2 <= bridge_count) { /* Just allow this channel to leave the multi-party bridge. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } ast_bridge_unlock(bridge_channel->bridge); return 0; @@ -2300,7 +2300,7 @@ static int bridge_personality_atxfer_push(struct ast_bridge *self, struct ast_br static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct attended_transfer_properties *props) { - if (self->num_channels > 1 || bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (self->num_channels > 1 || bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { return; } @@ -2794,13 +2794,13 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c if (!ast_strlen_zero(goto_on_blindxfr)) { ast_debug(1, "After transfer, transferer %s goes to %s\n", ast_channel_name(bridge_channel->chan), goto_on_blindxfr); - ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr); + ast_bridge_set_after_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr); } if (ast_bridge_transfer_blind(0, bridge_channel->chan, exten, context, blind_transfer_cb, bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS && !ast_strlen_zero(goto_on_blindxfr)) { - ast_after_bridge_goto_discard(bridge_channel->chan); + ast_bridge_discard_after_goto(bridge_channel->chan); } return 0; diff --git a/main/bridging_channel.c b/main/bridging_channel.c index 385545c2c..284f08732 100644 --- a/main/bridging_channel.c +++ b/main/bridging_channel.c @@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/timing.h" #include "asterisk/bridging.h" #include "asterisk/bridging_channel.h" +#include "asterisk/bridging_after.h" #include "asterisk/bridging_channel_internal.h" #include "asterisk/bridging_internal.h" #include "asterisk/stasis_bridging.h" @@ -67,18 +68,6 @@ 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); -struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request) -{ - struct ast_bridge *bridge; - - ast_bridge_channel_lock_bridge(bridge_channel); - bridge = bridge_channel->bridge; - ao2_ref(bridge, +1); - bridge_merge_inhibit_nolock(bridge, request); - ast_bridge_unlock(bridge); - return bridge; -} - void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge; @@ -101,6 +90,25 @@ void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel) } } +int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking) +{ + struct ast_frame action = { + .frametype = AST_FRAME_BRIDGE_ACTION, + .subclass.integer = started_talking + ? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &action); +} + +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state) +{ + ast_bridge_channel_lock(bridge_channel); + ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state); + ast_bridge_channel_unlock(bridge_channel); +} + +/*! \internal \brief Poke the bridge_channel thread */ static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel) { if (!pthread_equal(pthread_self(), bridge_channel->thread)) { @@ -111,6 +119,102 @@ static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel) } } +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state) +{ +/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { + return; + } + + ast_debug(1, "Setting %p(%s) state from:%d to:%d\n", + bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, + new_state); + + /* Change the state on the bridge channel */ + bridge_channel->state = new_state; + + bridge_channel_poke(bridge_channel); +} + +struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) +{ + struct ast_bridge *bridge = bridge_channel->bridge; + struct ast_bridge_channel *other = NULL; + + if (bridge_channel->in_bridge && bridge->num_channels == 2) { + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other != bridge_channel) { + break; + } + } + } + + return other; +} + +void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) +{ + /* Restore original formats of the channel as they came in */ + if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->read_format)); + if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { + ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->read_format)); + } + } + if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { + ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->write_format)); + if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { + ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", + bridge_channel, ast_channel_name(bridge_channel->chan), + ast_getformatname(&bridge_channel->write_format)); + } + } +} + +struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request) +{ + struct ast_bridge *bridge; + + ast_bridge_channel_lock_bridge(bridge_channel); + bridge = bridge_channel->bridge; + ao2_ref(bridge, +1); + bridge_merge_inhibit_nolock(bridge, request); + ast_bridge_unlock(bridge); + return bridge; +} + +void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +{ + struct ast_bridge_channel *other = NULL; + struct ast_bridge *bridge = bridge_channel->bridge; + const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan); + + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan)); + } + + if (ast_strlen_zero(oldest_linkedid)) { + return; + } + + ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid); + AST_LIST_TRAVERSE(&bridge->channels, other, entry) { + if (other == swap) { + continue; + } + ast_channel_linkedid_set(other->chan, oldest_linkedid); + } +} + void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -156,30 +260,120 @@ void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_ch } } -void ast_bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) +/*! +* \internal +* \brief Handle bridge hangup event. +* \since 12.0.0 +* +* \param bridge_channel Which channel is hanging up. +* +* \return Nothing +*/ +static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel) { - struct ast_bridge_channel *other = NULL; - struct ast_bridge *bridge = bridge_channel->bridge; - const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan); + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + struct ao2_iterator iter; - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { + /* Run any hangup hooks. */ + iter = ao2_iterator_init(features->other_hooks, 0); + for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { + int remove_me; + + if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) { continue; } - oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan)); + remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); + if (remove_me) { + ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + ao2_unlink(features->other_hooks, hook); + } } + ao2_iterator_destroy(&iter); - if (ast_strlen_zero(oldest_linkedid)) { - return; - } + /* Default hangup action. */ + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); +} - ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid); - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other == swap) { - continue; - } - ast_channel_linkedid_set(other->chan, oldest_linkedid); - } +/*! + * \internal + * \brief Write an \ref ast_frame onto the bridge channel + * \since 12.0.0 + * + * \param bridge_channel Which channel to queue the frame onto. + * \param frame The frame to write onto the bridge_channel + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + ast_bridge_channel_lock_bridge(bridge_channel); +/* + * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked). + * + * The tech decides if a frame needs to be pushed back for deferral. + * simple_bridge/native_bridge are likely the only techs that will do this. + */ + bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame); + ast_bridge_unlock(bridge_channel->bridge); + + /* + * Claim successful write to bridge. If deferred frame + * support is added, claim successfully deferred. + */ + return 0; +} + +/*! + * \internal + * \brief Queue an action frame onto the bridge channel with data. + * \since 12.0.0 + * + * \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(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, + .subclass.integer = action, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return ast_bridge_channel_queue_frame(bridge_channel, &frame); +} + +/*! + * \internal + * \brief Write an action frame onto the bridge channel with data. + * \since 12.0.0 + * + * \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_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, + .subclass.integer = action, + .datalen = datalen, + .data.ptr = (void *) data, + }; + + return bridge_channel_write_frame(bridge_channel, &frame); } int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr) @@ -206,7 +400,7 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st } ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { /* Drop frames on channels leaving the bridge. */ ast_bridge_channel_unlock(bridge_channel); ast_frfree(dup); @@ -222,42 +416,6 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st return 0; } -/*! - * \brief Queue an action frame onto the bridge channel with data. - * \since 12.0.0 - * - * \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 ast_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, - .subclass.integer = action, - .datalen = datalen, - .data.ptr = (void *) data, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &frame); -} - -int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) -{ - struct ast_frame frame = { - .frametype = AST_FRAME_CONTROL, - .subclass.integer = control, - .datalen = datalen, - .data.ptr = (void *) data, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &frame); -} - int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { struct ast_bridge_channel *cur; @@ -279,60 +437,16 @@ int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_ return not_written; } -void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel) -{ - /* Restore original formats of the channel as they came in */ - if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_debug(1, "Bridge is returning %p(%s) to read format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) { - ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->read_format)); - } - } - if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_debug(1, "Bridge is returning %p(%s) to write format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) { - ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n", - bridge_channel, ast_channel_name(bridge_channel->chan), - ast_getformatname(&bridge_channel->write_format)); - } - } -} - -static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) -{ - ast_bridge_channel_lock_bridge(bridge_channel); -/* - * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked). - * - * The tech decides if a frame needs to be pushed back for deferral. - * simple_bridge/native_bridge are likely the only techs that will do this. - */ - bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame); - ast_bridge_unlock(bridge_channel->bridge); - - /* - * Claim successful write to bridge. If deferred frame - * support is added, claim successfully deferred. - */ - return 0; -} - -static int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum bridge_channel_action_type action, const void *data, size_t datalen) +int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) { struct ast_frame frame = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = action, + .frametype = AST_FRAME_CONTROL, + .subclass.integer = control, .datalen = datalen, .data.ptr = (void *) data, }; - return bridge_channel_write_frame(bridge_channel, &frame); + return ast_bridge_channel_queue_frame(bridge_channel, &frame); } int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen) @@ -373,6 +487,7 @@ int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel) return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0); } +/*! \internal \brief Helper function to kick off a PBX app on a bridge_channel */ static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args) { int res = 0; @@ -394,42 +509,6 @@ static int run_app_helper(struct ast_channel *chan, const char *app_name, const return res; } -/*! -* \internal -* \brief Handle bridge hangup event. -* \since 12.0.0 -* -* \param bridge_channel Which channel is hanging up. -* -* \return Nothing -*/ -static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge_features *features = bridge_channel->features; - struct ast_bridge_hook *hook; - struct ao2_iterator iter; - - /* Run any hangup hooks. */ - iter = ao2_iterator_init(features->other_hooks, 0); - for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { - int remove_me; - - if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) { - continue; - } - remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt); - if (remove_me) { - ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n", - hook, bridge_channel, ast_channel_name(bridge_channel->chan)); - ao2_unlink(features->other_hooks, hook); - } - } - ao2_iterator_destroy(&iter); - - /* Default hangup action. */ - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); -} - void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { if (moh_class) { @@ -444,7 +523,6 @@ void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const } } - struct bridge_run_app { /*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */ int moh_offset; @@ -471,6 +549,10 @@ static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, st data->moh_offset ? &data->app_name[data->moh_offset] : NULL); } +/*! + * \internal + * \brief Marshal an application to be executed on a bridge_channel + */ static int payload_helper_app(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { @@ -497,13 +579,13 @@ static int payload_helper_app(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { - return payload_helper_app(ast_bridge_channel_write_action_data, + return payload_helper_app(bridge_channel_write_action_data, bridge_channel, app_name, app_args, moh_class); } int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { - return payload_helper_app(ast_bridge_channel_queue_action_data, + return payload_helper_app(bridge_channel_queue_action_data, bridge_channel, app_name, app_args, moh_class); } @@ -560,6 +642,10 @@ static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, s payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL); } +/*! + * \internal + * \brief Marshal a file to be played on a bridge_channel + */ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { @@ -582,13 +668,13 @@ static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { - return payload_helper_playfile(ast_bridge_channel_write_action_data, + return payload_helper_playfile(bridge_channel_write_action_data, bridge_channel, custom_play, playfile, moh_class); } int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { - return payload_helper_playfile(ast_bridge_channel_queue_action_data, + return payload_helper_playfile(bridge_channel_queue_action_data, bridge_channel, custom_play, playfile, moh_class); } @@ -618,6 +704,10 @@ static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size); } +/*! + * \internal + * \brief Marshal a custom callback function to be called on a bridge_channel + */ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) { @@ -644,13 +734,13 @@ static int payload_helper_cb(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) { - return payload_helper_cb(ast_bridge_channel_write_action_data, + return payload_helper_cb(bridge_channel_write_action_data, bridge_channel, callback, payload, payload_size); } int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) { - return payload_helper_cb(ast_bridge_channel_queue_action_data, + return payload_helper_cb(bridge_channel_queue_action_data, bridge_channel, callback, payload, payload_size); } @@ -661,6 +751,10 @@ struct bridge_park { char parkee_uuid[0]; }; +/*! + * \internal + * \brief Park a bridge_cahnnel + */ static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload) { ast_bridge_channel_park(bridge_channel, payload->parkee_uuid, @@ -668,6 +762,10 @@ static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struc payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL); } +/*! + * \internal + * \brief Marshal a park action onto a bridge_channel + */ static int payload_helper_park(ast_bridge_channel_post_action_data post_it, struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, @@ -694,50 +792,10 @@ static int payload_helper_park(ast_bridge_channel_post_action_data post_it, int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data) { - return payload_helper_park(ast_bridge_channel_write_action_data, + return payload_helper_park(bridge_channel_write_action_data, bridge_channel, parkee_uuid, parker_uuid, app_data); } -int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking) -{ - struct ast_frame action = { - .frametype = AST_FRAME_BRIDGE_ACTION, - .subclass.integer = started_talking - ? BRIDGE_CHANNEL_ACTION_TALKING_START : BRIDGE_CHANNEL_ACTION_TALKING_STOP, - }; - - return ast_bridge_channel_queue_frame(bridge_channel, &action); -} - -struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) -{ - struct ast_bridge *bridge = bridge_channel->bridge; - struct ast_bridge_channel *other = NULL; - - if (bridge_channel->in_bridge && bridge->num_channels == 2) { - AST_LIST_TRAVERSE(&bridge->channels, other, entry) { - if (other != bridge_channel) { - break; - } - } - } - - return other; -} - -struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan) -{ - struct ast_bridge_channel *bridge_channel; - - AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - if (bridge_channel->chan == chan) { - break; - } - } - - return bridge_channel; -} - /*! * \internal * \brief Suspend a channel from a bridge. @@ -748,7 +806,7 @@ struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct * * \return Nothing */ -void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_suspend_nolock(struct ast_bridge_channel *bridge_channel) { bridge_channel->suspended = 1; if (bridge_channel->in_bridge) { @@ -772,7 +830,7 @@ void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel) static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel) { ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel_suspend_nolock(bridge_channel); + bridge_channel_internal_suspend_nolock(bridge_channel); ast_bridge_unlock(bridge_channel->bridge); } @@ -786,7 +844,7 @@ static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel) * * \return Nothing */ -void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) { bridge_channel->suspended = 0; if (bridge_channel->in_bridge) { @@ -815,7 +873,7 @@ void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel) static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel) { ast_bridge_channel_lock_bridge(bridge_channel); - bridge_channel_unsuspend_nolock(bridge_channel); + bridge_channel_internal_unsuspend_nolock(bridge_channel); ast_bridge_unlock(bridge_channel->bridge); } @@ -913,14 +971,15 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha } } +/*! \internal \brief Write a DTMF stream out to a channel */ static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf) { - return ast_bridge_channel_write_action_data(bridge_channel, + return bridge_channel_write_action_data(bridge_channel, BRIDGE_CHANNEL_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1); } /*! - * \brief Internal function that executes a feature on a bridge channel + * \internal \brief Internal function that executes a feature on a bridge channel * \note Neither the bridge nor the bridge_channel locks should be held when entering * this function. */ @@ -1017,6 +1076,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel) } } +/*! \internal \brief Indicate that a bridge_channel is talking */ static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking) { struct ast_bridge_features *features = bridge_channel->features; @@ -1051,13 +1111,13 @@ static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0); } -static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel, - struct blind_transfer_data *blind_data) -{ - ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); - bridge_channel_handle_hangup(bridge_channel); -} +/*! \brief Data specifying where a blind transfer is going to */ +struct blind_transfer_data { + char exten[AST_MAX_EXTENSION]; + char context[AST_MAX_CONTEXT]; +}; +/*! \internal \brief Execute after bridge actions on a channel when it leaves a bridge */ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data) { RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); @@ -1094,15 +1154,25 @@ static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *da ast_party_connected_line_free(&connected_target); } -static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data) +/*! \internal \brief Execute logic to cleanup when after bridge fails */ +static void after_bridge_move_channel_fail(enum ast_bridge_after_cb_reason reason, void *data) { RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); ast_log(LOG_WARNING, "Unable to complete transfer: %s\n", - ast_after_bridge_cb_reason_string(reason)); + ast_bridge_after_cb_reason_string(reason)); ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); } +/*! \internal \brief Perform a blind transfer on a channel in a bridge */ +static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel, + struct blind_transfer_data *blind_data) +{ + ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); + bridge_channel_handle_hangup(bridge_channel); +} + +/*! \internal \brief Perform an attended transfer on a channel in a bridge */ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel, const char *target_chan_name) { @@ -1122,11 +1192,11 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c ao2_ref(chan_bridged, +1); ast_bridge_channel_unlock(bridge_channel); - if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel, + if (ast_bridge_set_after_callback(chan_bridged, after_bridge_move_channel, after_bridge_move_channel_fail, ast_channel_ref(chan_target))) { ast_softhangup(chan_target, AST_SOFTHANGUP_DEV); - /* Release the ref we tried to pass to ast_after_bridge_callback_set(). */ + /* Release the ref we tried to pass to ast_bridge_set_after_callback(). */ ast_channel_unref(chan_target); } bridge_channel_handle_hangup(bridge_channel); @@ -1214,7 +1284,7 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann * * \return Nothing */ -static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) +static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -1230,7 +1300,7 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) } switch (bridge_channel->state) { - case AST_BRIDGE_CHANNEL_STATE_END: + case BRIDGE_CHANNEL_STATE_END: /* Do we need to dissolve the bridge because this channel hung up? */ if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP) || (bridge_channel->features->usable @@ -1246,18 +1316,7 @@ static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel) /* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */ } -/*! - * \internal - * \brief Pull the bridge channel out of its current bridge. - * \since 12.0.0 - * - * \param bridge_channel Channel to pull. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \return Nothing - */ -void bridge_channel_pull(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -1306,25 +1365,13 @@ void bridge_channel_pull(struct ast_bridge_channel *bridge_channel) ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING); } - bridge_dissolve_check(bridge_channel); + bridge_channel_dissolve_check(bridge_channel); bridge->reconfigured = 1; ast_bridge_publish_leave(bridge, bridge_channel->chan); } -/*! - * \internal - * \brief Push the bridge channel into its specified bridge. - * \since 12.0.0 - * - * \param bridge_channel Channel to push. - * - * \note On entry, bridge_channel->bridge is already locked. - * - * \retval 0 on success. - * \retval -1 on failure. The channel did not get pushed. - */ -int bridge_channel_push(struct ast_bridge_channel *bridge_channel) +int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; struct ast_bridge_channel *swap; @@ -1345,8 +1392,8 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel) /* Add channel to the bridge */ if (bridge->dissolved - || bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT - || (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT) + || bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT + || (swap && swap->state != BRIDGE_CHANNEL_STATE_WAIT) || bridge->v_table->push(bridge, bridge_channel, swap) || ast_bridge_channel_establish_roles(bridge_channel)) { ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n", @@ -1373,8 +1420,8 @@ int bridge_channel_push(struct ast_bridge_channel *bridge_channel) ast_bridge_publish_enter(bridge, bridge_channel->chan); if (swap) { - ast_bridge_channel_leave_bridge(swap, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); - bridge_channel_pull(swap); + ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + bridge_channel_internal_pull(swap); } /* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */ @@ -1517,7 +1564,7 @@ static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channe break; default: /* Write the frame to the channel. */ - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE; + bridge_channel->activity = BRIDGE_CHANNEL_THREAD_SIMPLE; ast_write(bridge_channel->chan, fr); break; } @@ -1661,7 +1708,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) /* Wait for data to either come from the channel or us to be signaled */ ast_bridge_channel_lock(bridge_channel); - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { } else if (bridge_channel->suspended) { /* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */ ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n", @@ -1687,10 +1734,10 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) ast_bridge_unlock(bridge_channel->bridge); } ast_bridge_channel_lock(bridge_channel); - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME; + bridge_channel->activity = BRIDGE_CHANNEL_THREAD_FRAME; ast_bridge_channel_unlock(bridge_channel); if (!bridge_channel->suspended - && bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + && bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { if (chan) { bridge_handle_trip(bridge_channel); } else if (-1 < outfd) { @@ -1700,7 +1747,7 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel) bridge_channel_handle_interval(bridge_channel); } } - bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE; + bridge_channel->activity = BRIDGE_CHANNEL_THREAD_IDLE; return; } ast_bridge_channel_unlock(bridge_channel); @@ -1747,7 +1794,7 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch } /*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */ -void bridge_channel_join(struct ast_bridge_channel *bridge_channel) +void bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) { ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan)); ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan)); @@ -1777,12 +1824,12 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) bridge_channel->bridge->callid = ast_read_threadstorage_callid(); } - if (bridge_channel_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + if (bridge_channel_internal_push(bridge_channel)) { + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); } bridge_reconfigured(bridge_channel->bridge, 1); - if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* * Indicate a source change since this channel is entering the * bridge system only if the bridge technology is not MULTIMIX @@ -1795,7 +1842,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) ast_bridge_unlock(bridge_channel->bridge); bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_JOIN); - while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + while (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* Wait for something to do. */ bridge_channel_wait(bridge_channel); } @@ -1803,7 +1850,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) ast_bridge_channel_lock_bridge(bridge_channel); } - bridge_channel_pull(bridge_channel); + bridge_channel_internal_pull(bridge_channel); bridge_reconfigured(bridge_channel->bridge, 1); ast_bridge_unlock(bridge_channel->bridge); @@ -1836,48 +1883,7 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel) ast_bridge_channel_restore_formats(bridge_channel); } -void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) -{ - ast_bridge_channel_lock(bridge_channel); - ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state); - ast_bridge_channel_unlock(bridge_channel); -} - -void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state) -{ -/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ - if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) { - return; - } - - ast_debug(1, "Setting %p(%s) state from:%d to:%d\n", - bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, - new_state); - - /* Change the state on the bridge channel */ - bridge_channel->state = new_state; - - bridge_channel_poke(bridge_channel); -} - -/*! - * \internal - * \brief Queue a blind transfer action on a transferee bridge channel - * - * This is only relevant for when a blind transfer is performed on a two-party - * bridge. The transferee's bridge channel will have a blind transfer bridge - * action queued onto it, resulting in the party being redirected to a new - * destination - * - * \param transferee The channel to have the action queued on - * \param exten The destination extension for the transferee - * \param context The destination context for the transferee - * \param hook Frame hook to attach to transferee - * - * \retval 0 on success. - * \retval -1 on error. - */ -int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, +int bridge_channel_internal_queue_blind_transfer(struct ast_channel *transferee, const char *exten, const char *context, transfer_channel_cb new_channel_cb, void *user_data) { @@ -1899,11 +1905,11 @@ int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, ast_copy_string(blind_data.exten, exten, sizeof(blind_data.exten)); ast_copy_string(blind_data.context, context, sizeof(blind_data.context)); - return ast_bridge_channel_queue_action_data(transferee_bridge_channel, + return bridge_channel_queue_action_data(transferee_bridge_channel, BRIDGE_CHANNEL_ACTION_BLIND_TRANSFER, &blind_data, sizeof(blind_data)); } -int bridge_channel_queue_attended_transfer(struct ast_channel *transferee, +int bridge_channel_internal_queue_attended_transfer(struct ast_channel *transferee, struct ast_channel *unbridged_chan) { RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup); @@ -1920,7 +1926,114 @@ int bridge_channel_queue_attended_transfer(struct ast_channel *transferee, ast_copy_string(unbridged_chan_name, ast_channel_name(unbridged_chan), sizeof(unbridged_chan_name)); - return ast_bridge_channel_queue_action_data(transferee_bridge_channel, + return bridge_channel_queue_action_data(transferee_bridge_channel, BRIDGE_CHANNEL_ACTION_ATTENDED_TRANSFER, unbridged_chan_name, sizeof(unbridged_chan_name)); } + +int bridge_channel_internal_allows_optimization(struct ast_bridge_channel *bridge_channel) +{ + return bridge_channel->in_bridge + && AST_LIST_EMPTY(&bridge_channel->wr_queue); +} + +/*! + * \internal + * \brief Close a pipe. + * \since 12.0.0 + * + * \param my_pipe What to close. + * + * \return Nothing + */ +static void pipe_close(int *my_pipe) +{ + if (my_pipe[0] > -1) { + close(my_pipe[0]); + my_pipe[0] = -1; + } + if (my_pipe[1] > -1) { + close(my_pipe[1]); + my_pipe[1] = -1; + } +} + +/*! + * \internal + * \brief Initialize a pipe as non-blocking. + * \since 12.0.0 + * + * \param my_pipe What to initialize. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int pipe_init_nonblock(int *my_pipe) +{ + int flags; + + my_pipe[0] = -1; + my_pipe[1] = -1; + if (pipe(my_pipe)) { + ast_log(LOG_WARNING, "Can't create pipe! Try increasing max file descriptors with ulimit -n\n"); + return -1; + } + flags = fcntl(my_pipe[0], F_GETFL); + if (fcntl(my_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Unable to set read pipe nonblocking! (%d: %s)\n", + errno, strerror(errno)); + return -1; + } + flags = fcntl(my_pipe[1], F_GETFL); + if (fcntl(my_pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) { + ast_log(LOG_WARNING, "Unable to set write pipe nonblocking! (%d: %s)\n", + errno, strerror(errno)); + return -1; + } + return 0; +} + +/* Destroy elements of the bridge channel structure and the bridge channel structure itself */ +static void bridge_channel_destroy(void *obj) +{ + struct ast_bridge_channel *bridge_channel = obj; + struct ast_frame *fr; + + if (bridge_channel->callid) { + bridge_channel->callid = ast_callid_unref(bridge_channel->callid); + } + + if (bridge_channel->bridge) { + ao2_ref(bridge_channel->bridge, -1); + bridge_channel->bridge = NULL; + } + + /* Flush any unhandled wr_queue frames. */ + while ((fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list))) { + ast_frfree(fr); + } + pipe_close(bridge_channel->alert_pipe); + + ast_cond_destroy(&bridge_channel->cond); +} + +struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *bridge_channel; + + bridge_channel = ao2_alloc(sizeof(struct ast_bridge_channel), bridge_channel_destroy); + if (!bridge_channel) { + return NULL; + } + ast_cond_init(&bridge_channel->cond, NULL); + if (pipe_init_nonblock(bridge_channel->alert_pipe)) { + ao2_ref(bridge_channel, -1); + return NULL; + } + if (bridge) { + bridge_channel->bridge = bridge; + ao2_ref(bridge_channel->bridge, +1); + } + + return bridge_channel; +} diff --git a/main/features.c b/main/features.c index ddbd04b46..d1b33d5b9 100644 --- a/main/features.c +++ b/main/features.c @@ -73,6 +73,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/test.h" #include "asterisk/bridging.h" #include "asterisk/bridging_basic.h" +#include "asterisk/bridging_after.h" #include "asterisk/stasis.h" #include "asterisk/stasis_channels.h" #include "asterisk/features_config.h" @@ -839,15 +840,15 @@ static void *bridge_call_thread(void *data) } if (tobj->return_to_pbx) { - ast_after_bridge_set_goto(tobj->chan, ast_channel_context(tobj->chan), + ast_bridge_set_after_goto(tobj->chan, ast_channel_context(tobj->chan), ast_channel_exten(tobj->chan), ast_channel_priority(tobj->chan)); - ast_after_bridge_set_goto(tobj->peer, ast_channel_context(tobj->peer), + ast_bridge_set_after_goto(tobj->peer, ast_channel_context(tobj->peer), ast_channel_exten(tobj->peer), ast_channel_priority(tobj->peer)); } ast_bridge_call(tobj->chan, tobj->peer, &tobj->bconfig); - ast_after_bridge_goto_run(tobj->chan); + ast_bridge_run_after_goto(tobj->chan); ast_free(tobj); @@ -3544,7 +3545,7 @@ static void bridge_check_monitor(struct ast_channel *chan, struct ast_channel *p */ static void bridge_failed_peer_goto(struct ast_channel *chan, struct ast_channel *peer) { - if (ast_after_bridge_goto_setup(peer) + if (ast_bridge_setup_after_goto(peer) || ast_pbx_start(peer)) { ast_autoservice_chan_hangup_peer(chan, peer); } @@ -4399,7 +4400,7 @@ static int action_bridge(struct mansession *s, const struct message *m) return 0; } - ast_after_bridge_set_go_on(chana, chana_context, chana_exten, chana_priority, NULL); + ast_bridge_set_after_go_on(chana, chana_context, chana_exten, chana_priority, NULL); if (ast_bridge_add_channel(bridge, chana, NULL, playtone & PLAYTONE_CHANNEL1, xfer_cfg_a ? xfer_cfg_a->xfersound : NULL)) { snprintf(buf, sizeof(buf), "Unable to add Channel1 to bridge: %s", ast_channel_name(chana)); astman_send_error(s, m, buf); @@ -4407,7 +4408,7 @@ static int action_bridge(struct mansession *s, const struct message *m) return 0; } - ast_after_bridge_set_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL); + ast_bridge_set_after_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL); if (ast_bridge_add_channel(bridge, chanb, NULL, playtone & PLAYTONE_CHANNEL2, xfer_cfg_b ? xfer_cfg_b->xfersound : NULL)) { snprintf(buf, sizeof(buf), "Unable to add Channel2 to bridge: %s", ast_channel_name(chanb)); astman_send_error(s, m, buf); @@ -5046,7 +5047,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) extension = ast_strdupa(ast_channel_exten(chan)); priority = ast_channel_priority(chan); ast_channel_unlock(chan); - ast_after_bridge_set_go_on(current_dest_chan, context, extension, priority, + ast_bridge_set_after_go_on(current_dest_chan, context, extension, priority, opt_args[OPT_ARG_CALLEE_GO_ON]); } else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) { ast_channel_lock(current_dest_chan); @@ -5054,7 +5055,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) extension = ast_strdupa(ast_channel_exten(current_dest_chan)); priority = ast_channel_priority(current_dest_chan); ast_channel_unlock(current_dest_chan); - ast_after_bridge_set_goto(current_dest_chan, context, extension, priority); + ast_bridge_set_after_goto(current_dest_chan, context, extension, priority); } if (ast_bridge_features_init(&chan_features)) { |