diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c index d4446ceb523dbd72177502e47cb56f42ff569c19..cb5e0ce179744ebaad31e5f63471b2a356b34411 100644 --- a/apps/app_agent_pool.c +++ b/apps/app_agent_pool.c @@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/astobj2.h" #include "asterisk/stringfields.h" #include "asterisk/stasis_channels.h" +#include "asterisk/causes.h" /*** DOCUMENTATION <application name="AgentLogin" language="en_US"> @@ -760,7 +761,7 @@ static void agent_pvt_destructor(void *vdoomed) ast_party_connected_line_free(&doomed->waiting_colp); if (doomed->caller_bridge) { - ast_bridge_destroy(doomed->caller_bridge); + ast_bridge_destroy(doomed->caller_bridge, AST_CAUSE_USER_BUSY); doomed->caller_bridge = NULL; } if (doomed->logged) { @@ -1019,15 +1020,17 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru if (!caller_bridge) { /* Reset agent. */ - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); return; } res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan, NULL, 0); if (res) { /* Reset agent. */ - ast_bridge_destroy(caller_bridge); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); return; } ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0); @@ -1122,13 +1125,15 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge_channel *bridge_channel if (deferred_logoff) { ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); } else if (probation_timedout) { ast_debug(1, "Agent %s: Login complete.\n", agent->username); agent_devstate_changed(agent->username); } else if (ack_timedout) { ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); } else if (wrapup_timedout) { ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username); agent_devstate_changed(agent->username); @@ -1233,7 +1238,8 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha * agent will have some slightly different behavior in corner * cases. */ - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); return 0; } @@ -1455,7 +1461,7 @@ static void agent_logout(struct agent_pvt *agent) agent_devstate_changed(agent->username); if (caller_bridge) { - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY); } send_agent_logoff(logged, agent->username, time_logged_in); @@ -1479,6 +1485,7 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged) struct ast_bridge_features features; if (ast_bridge_features_init(&features)) { + ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING); goto agent_run_cleanup; } for (;;) { @@ -1488,6 +1495,8 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged) struct ast_bridge *holding; struct ast_bridge *caller_bridge; + ast_channel_hangupcause_set(logged, AST_CAUSE_NORMAL_CLEARING); + holding = ao2_global_obj_ref(agent_holding); if (!holding) { ast_debug(1, "Agent %s: Someone destroyed the agent holding bridge.\n", @@ -1535,7 +1544,7 @@ static void agent_run(struct agent_pvt *agent, struct ast_channel *logged) agent_unlock(agent); ao2_ref(cfg_old, -1); if (caller_bridge) { - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY); } if (agent->state == AGENT_STATE_LOGGING_OUT @@ -1661,13 +1670,14 @@ static void caller_abort_agent(struct agent_pvt *agent) agent->caller_bridge = NULL; agent_unlock(agent); if (caller_bridge) { - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, AST_CAUSE_USER_BUSY); } return; } /* Kick the agent out of the holding bridge to reset it. */ - ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge_nolock(logged, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); ast_bridge_channel_unlock(logged); } @@ -1677,7 +1687,8 @@ static int caller_safety_timeout(struct ast_bridge_channel *bridge_channel, void if (agent->state == AGENT_STATE_CALL_PRESENT) { ast_verb(3, "Agent '%s' did not respond. Safety timeout.\n", agent->username); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_USER_BUSY); caller_abort_agent(agent); } @@ -1829,7 +1840,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data) case AGENT_STATE_LOGGING_OUT: agent_unlock(agent); ast_party_connected_line_free(&connected); - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, 0); ast_bridge_features_cleanup(&caller_features); ast_verb(3, "Agent '%s' not logged in.\n", agent->username); pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN"); @@ -1843,7 +1854,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data) default: agent_unlock(agent); ast_party_connected_line_free(&connected); - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, 0); ast_bridge_features_cleanup(&caller_features); ast_verb(3, "Agent '%s' is busy.\n", agent->username); pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "BUSY"); @@ -1855,7 +1866,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data) logged = agent_bridge_channel_get_lock(agent); if (!logged) { ast_party_connected_line_free(&connected); - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, 0); ast_bridge_features_cleanup(&caller_features); ast_verb(3, "Agent '%s' not logged in.\n", agent->username); pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "NOT_LOGGED_IN"); @@ -1870,7 +1881,7 @@ static int agent_request_exec(struct ast_channel *chan, const char *data) ast_bridge_channel_unlock(logged); ao2_ref(logged, -1); if (res) { - ast_bridge_destroy(caller_bridge); + ast_bridge_destroy(caller_bridge, 0); ast_bridge_features_cleanup(&caller_features); ast_verb(3, "Agent '%s': Failed to alert the agent.\n", agent->username); pbx_builtin_setvar_helper(chan, "AGENT_STATUS", "ERROR"); @@ -2499,7 +2510,7 @@ static int unload_module(void) /* Destroy agent holding bridge. */ holding = ao2_global_obj_replace(agent_holding, NULL); if (holding) { - ast_bridge_destroy(holding); + ast_bridge_destroy(holding, 0); } destroy_config(); diff --git a/apps/app_bridgewait.c b/apps/app_bridgewait.c index 36747056e4d8ceaca62856cd1f4f8ffa9414d738..8e615f05ff2ae40693d45a7568cd7ad529ce21d7 100644 --- a/apps/app_bridgewait.c +++ b/apps/app_bridgewait.c @@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/bridge.h" #include "asterisk/musiconhold.h" #include "asterisk/astobj2.h" +#include "asterisk/causes.h" /*** DOCUMENTATION <application name="BridgeWait" language="en_US"> @@ -127,8 +128,9 @@ struct wait_bridge_wrapper { static void wait_bridge_wrapper_destructor(void *obj) { struct wait_bridge_wrapper *wrapper = obj; + if (wrapper->bridge) { - ast_bridge_destroy(wrapper->bridge); + ast_bridge_destroy(wrapper->bridge, 0); } } @@ -204,7 +206,8 @@ AST_APP_OPTIONS(bridgewait_opts, { static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt) { ast_verb(3, "Channel %s timed out.\n", ast_channel_name(bridge_channel->chan)); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); return -1; } @@ -321,9 +324,8 @@ static struct wait_bridge_wrapper *wait_bridge_wrapper_alloc(const char *bridge_ bridge_wrapper = ao2_alloc_options(sizeof(*bridge_wrapper) + strlen(bridge_name) + 1, wait_bridge_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); - if (!bridge_wrapper) { - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); return NULL; } diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c index 79631225e271c21cfdd833cba36174333fbc2269..d9db7ab5b6c9066fbd49e3be4db7b9579fcc8cbd 100644 --- a/apps/app_confbridge.c +++ b/apps/app_confbridge.c @@ -892,7 +892,7 @@ static void destroy_conference_bridge(void *obj) /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */ if (conference->bridge) { - ast_bridge_destroy(conference->bridge); + ast_bridge_destroy(conference->bridge, 0); conference->bridge = NULL; } diff --git a/apps/app_dial.c b/apps/app_dial.c index 4397c5090f2eea599e39888ca52e6463944cdd70..c053820dcc1333ead5eab30474f30235a5c76b3a 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -3024,7 +3024,6 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0); } -/* BUGBUG bridge needs to set hangup cause on chan when peer breaks the bridge. */ setup_peer_after_bridge_goto(chan, peer, &opts, opt_args); res = ast_bridge_call(chan, peer, &config); } diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index c22b83548091cc3276c72fe378983d119d1c390c..ee4a69682b64070d9073e871b4356dd6a27bfd7b 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -53,6 +53,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/monitor.h" #include "asterisk/mixmonitor.h" #include "asterisk/audiohook.h" +#include "asterisk/causes.h" enum set_touch_variables_res { SET_TOUCH_SUCCESS, @@ -487,7 +488,8 @@ static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_ * bridge_channel to force the channel out of the bridge and the * core takes care of the rest. */ - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); return 0; } diff --git a/bridges/bridge_builtin_interval_features.c b/bridges/bridge_builtin_interval_features.c index edaddf3ea93b5274f4ad51ddf8e66e897c9ed29b..e804729bfa7d425bfb26498e06e2a44ac4ba76b3 100644 --- a/bridges/bridge_builtin_interval_features.c +++ b/bridges/bridge_builtin_interval_features.c @@ -45,10 +45,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/astobj2.h" #include "asterisk/test.h" - #include "asterisk/say.h" #include "asterisk/stringfields.h" #include "asterisk/musiconhold.h" +#include "asterisk/causes.h" static int bridge_features_duration_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt) { @@ -58,9 +58,11 @@ static int bridge_features_duration_callback(struct ast_bridge_channel *bridge_c ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, AST_DIGIT_NONE); } - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, + AST_CAUSE_NORMAL_CLEARING); - ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s", ast_channel_name(bridge_channel->chan)); + ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s", + ast_channel_name(bridge_channel->chan)); return -1; } diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h index 9522962bc44dd9538aa055d40c95cef314f89b54..d351b0feab0909d58368caad7618ccbec097e3e3 100644 --- a/include/asterisk/bridge.h +++ b/include/asterisk/bridge.h @@ -304,6 +304,8 @@ struct ast_bridge { * \note Temporary as in try again in a moment. */ unsigned int inhibit_merge; + /*! Cause code of the dissolved bridge. */ + int cause; /*! TRUE if the bridge was reconfigured. */ unsigned int reconfigured:1; /*! TRUE if the bridge has been dissolved. Any channel that now tries to join is immediately ejected. */ @@ -395,6 +397,7 @@ static inline void _ast_bridge_unlock(struct ast_bridge *bridge, const char *fil * \brief Destroy a bridge * * \param bridge Bridge to destroy + * \param cause Cause of bridge being destroyed. (If cause <= 0 then use AST_CAUSE_NORMAL_CLEARING) * * \retval 0 on success * \retval -1 on failure @@ -402,12 +405,12 @@ static inline void _ast_bridge_unlock(struct ast_bridge *bridge, const char *fil * Example usage: * * \code - * ast_bridge_destroy(bridge); + * ast_bridge_destroy(bridge, AST_CAUSE_NORMAL_CLEARING); * \endcode * * This destroys a bridge that was previously created. */ -int ast_bridge_destroy(struct ast_bridge *bridge); +int ast_bridge_destroy(struct ast_bridge *bridge, int cause); /*! * \brief Notify bridging that this channel was just masqueraded. diff --git a/include/asterisk/bridge_channel.h b/include/asterisk/bridge_channel.h index 2d1dfd4f0e22a5f5d2f1e116b82b7170e2f6928c..ae5131cef21bfce4d576d4064c08db4bbd84e9c4 100644 --- a/include/asterisk/bridge_channel.h +++ b/include/asterisk/bridge_channel.h @@ -239,36 +239,40 @@ int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, * * \param bridge_channel Channel to change the state on * \param new_state The new state to place the channel into + * \param cause Cause of channel leaving bridge. + * If cause <= 0 then use cause on channel if cause still <= 0 use AST_CAUSE_NORMAL_CLEARING. * * Example usage: * * \code - * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, AST_CAUSE_NORMAL_CLEARING); * \endcode * * This places the channel pointed to by bridge_channel into the * state BRIDGE_CHANNEL_STATE_END if it was * BRIDGE_CHANNEL_STATE_WAIT before. */ -void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state); +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause); /*! * \brief Set bridge channel state to leave bridge (if not leaving already). * * \param bridge_channel Channel to change the state on * \param new_state The new state to place the channel into + * \param cause Cause of channel leaving bridge. + * If cause <= 0 then use cause on channel if cause still <= 0 use AST_CAUSE_NORMAL_CLEARING. * * Example usage: * * \code - * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + * ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, AST_CAUSE_NORMAL_CLEARING); * \endcode * * This places the channel pointed to by bridge_channel into the * state BRIDGE_CHANNEL_STATE_END if it was * BRIDGE_CHANNEL_STATE_WAIT before. */ -void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state); +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause); /*! * \brief Get the peer bridge channel of a two party bridge. @@ -599,13 +603,15 @@ int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, con * \since 12.0.0 * * \param bridge_channel Which channel is being kicked or hungup. + * \param cause Cause of channel being kicked. + * If cause <= 0 then use cause on channel if cause still <= 0 use AST_CAUSE_NORMAL_CLEARING. * * \note This is intended to be called by bridge hooks and the * bridge channel thread. * * \return Nothing */ -void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel); +void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int cause); #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/include/asterisk/bridge_internal.h b/include/asterisk/bridge_internal.h index 4ff2fd2aad84758ec27204735af66d7cde2b0cfa..871abb8c5002de008771e88d16d172a6fe30a97a 100644 --- a/include/asterisk/bridge_internal.h +++ b/include/asterisk/bridge_internal.h @@ -198,6 +198,7 @@ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update); * \since 12.0.0 * * \param bridge Bridge to eject all channels + * \param cause Cause of bridge being dissolved. (If cause <= 0 then use AST_CAUSE_NORMAL_CLEARING) * * \details * Force out all channels that are not already going out of the @@ -207,6 +208,6 @@ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update); * * \return Nothing */ -void bridge_dissolve(struct ast_bridge *bridge); +void bridge_dissolve(struct ast_bridge *bridge, int cause); #endif /* _ASTERISK_PRIVATE_BRIDGING_H */ diff --git a/main/bridge.c b/main/bridge.c index e472bfcd306613a93fcae6b4df9c2b4fa197911e..3456be26e7aa12d6b33235db2bfb5a1bb6e6cd6f 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -55,7 +55,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/test.h" #include "asterisk/_private.h" - #include "asterisk/heap.h" #include "asterisk/say.h" #include "asterisk/timing.h" @@ -66,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/parking.h" #include "asterisk/core_local.h" #include "asterisk/core_unreal.h" +#include "asterisk/causes.h" /*! All bridges container. */ static struct ao2_container *bridges; @@ -241,22 +241,7 @@ int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action) return 0; } -/*! - * \internal - * \brief Dissolve the bridge. - * \since 12.0.0 - * - * \param bridge Bridge to eject all channels - * - * \details - * Force out all channels that are not already going out of the - * bridge. Any new channels joining will leave immediately. - * - * \note On entry, bridge is already locked. - * - * \return Nothing - */ -void bridge_dissolve(struct ast_bridge *bridge) +void bridge_dissolve(struct ast_bridge *bridge, int cause) { struct ast_bridge_channel *bridge_channel; struct ast_frame action = { @@ -269,11 +254,17 @@ void bridge_dissolve(struct ast_bridge *bridge) } bridge->dissolved = 1; - ast_debug(1, "Bridge %s: dissolving bridge\n", bridge->uniqueid); + if (cause <= 0) { + cause = AST_CAUSE_NORMAL_CLEARING; + } + bridge->cause = cause; + + ast_debug(1, "Bridge %s: dissolving bridge with cause %d(%s)\n", + bridge->uniqueid, cause, ast_cause2str(cause)); -/* 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, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, cause); } /* Must defer dissolving bridge because it is already locked. */ @@ -302,7 +293,7 @@ static void bridge_dissolve_check_stolen(struct ast_bridge *bridge, struct ast_b && ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP)) { /* The stolen channel controlled the bridge it was stolen from. */ - bridge_dissolve(bridge); + bridge_dissolve(bridge, 0); return; } if (bridge->num_channels < 2 @@ -311,7 +302,7 @@ static void bridge_dissolve_check_stolen(struct ast_bridge *bridge, struct ast_b * The stolen channel has not left enough channels to keep the * bridge alive. Assume the stolen channel hung up. */ - bridge_dissolve(bridge); + bridge_dissolve(bridge, 0); return; } } @@ -648,7 +639,7 @@ struct ast_bridge *bridge_register(struct ast_bridge *bridge) bridge->construction_completed = 1; ast_bridge_publish_state(bridge); if (!ao2_link(bridges, bridge)) { - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); bridge = NULL; } } @@ -852,11 +843,11 @@ struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags return bridge; } -int ast_bridge_destroy(struct ast_bridge *bridge) +int ast_bridge_destroy(struct ast_bridge *bridge, int cause) { ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid); ast_bridge_lock(bridge); - bridge_dissolve(bridge); + bridge_dissolve(bridge, cause); ast_bridge_unlock(bridge); ao2_ref(bridge, -1); @@ -1366,7 +1357,7 @@ void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update) if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART) && smart_bridge_operation(bridge)) { /* Smart bridge failed. */ - bridge_dissolve(bridge); + bridge_dissolve(bridge, 0); return; } bridge_complete_join(bridge); @@ -1652,7 +1643,8 @@ int ast_bridge_depart(struct ast_channel *chan) * channel thread. */ - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); /* Wait for the depart thread to die */ ast_debug(1, "Waiting for %p(%s) bridge thread to die.\n", @@ -1680,7 +1672,8 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan) return -1; } - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); ast_bridge_unlock(bridge); @@ -1689,7 +1682,7 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan) static void kick_it(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size) { - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING); } int ast_bridge_kick(struct ast_bridge *bridge, struct ast_channel *chan) @@ -1770,7 +1763,8 @@ 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, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); break; } } @@ -1788,7 +1782,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg bridge_channel_change_bridge(bridge_channel, dst_bridge); if (bridge_channel_internal_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause); } } AST_LIST_TRAVERSE_SAFE_END; @@ -1802,7 +1797,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg bridge_channel = kick_me[idx]; ast_bridge_channel_lock(bridge_channel); if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { - ast_bridge_channel_leave_bridge_nolock(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge_nolock(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); bridge_channel_internal_pull(bridge_channel); } ast_bridge_channel_unlock(bridge_channel); @@ -2036,10 +2032,12 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri bridge_channel_change_bridge(bridge_channel, orig_bridge); if (bridge_channel_internal_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause); } } else { - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause); } res = -1; } @@ -2452,7 +2450,8 @@ 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, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(src_bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); res = -1; } if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) { @@ -4063,7 +4062,8 @@ 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, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(source_bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); return AST_BRIDGE_TRANSFER_SUCCESS; } else { return AST_BRIDGE_TRANSFER_INVALID; @@ -4619,7 +4619,7 @@ static char *handle_bridge_destroy_specific(struct ast_cli_entry *e, int cmd, st } ast_cli(a->fd, "Destroying bridge '%s'\n", a->argv[2]); - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); return CLI_SUCCESS; } diff --git a/main/bridge_basic.c b/main/bridge_basic.c index 283387a422f62ec65caabac9d275371c8c0b458a..103099d8b3af0375db5a3ec32abf9390f7a05c2c 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -299,7 +299,8 @@ static int basic_hangup_hook(struct ast_bridge_channel *bridge_channel, void *ho } if (2 <= bridge_count) { /* Just allow this channel to leave the multi-party bridge. */ - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0); } ast_bridge_unlock(bridge_channel->bridge); return 0; @@ -1488,7 +1489,7 @@ static void attended_transfer_properties_shutdown(struct attended_transfer_prope } if (props->target_bridge) { - ast_bridge_destroy(props->target_bridge); + ast_bridge_destroy(props->target_bridge, 0); props->target_bridge = NULL; } @@ -2454,7 +2455,7 @@ static enum attended_transfer_state wait_to_recall_exit(struct attended_transfer static int fail_enter(struct attended_transfer_properties *props) { if (props->transferee_bridge) { - ast_bridge_destroy(props->transferee_bridge); + ast_bridge_destroy(props->transferee_bridge, 0); props->transferee_bridge = NULL; } return 0; diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 0759634a0a00e8810f24ca32e32f96ef722039f7..385be6d3eb3aa66aaa541c4b3a35dd7ae3e1cd28 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -53,6 +53,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/musiconhold.h" #include "asterisk/features_config.h" #include "asterisk/parking.h" +#include "asterisk/causes.h" /*! * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge. @@ -101,13 +102,6 @@ int ast_bridge_channel_notify_talking(struct ast_bridge_channel *bridge_channel, 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 @@ -122,9 +116,33 @@ 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) +/*! + * \internal + * \brief Set actual cause on channel. + * \since 12.0.0 + * + * \param chan Channel to set cause. + * \param cause Cause to set on channel. + * If cause <= 0 then use cause on channel if cause still <= 0 use AST_CAUSE_NORMAL_CLEARING. + * + * \return Actual cause set on channel. + */ +static int channel_set_cause(struct ast_channel *chan, int cause) +{ + ast_channel_lock(chan); + if (cause <= 0) { + cause = ast_channel_hangupcause(chan); + if (cause <= 0) { + cause = AST_CAUSE_NORMAL_CLEARING; + } + } + ast_channel_hangupcause_set(chan, cause); + ast_channel_unlock(chan); + return cause; +} + +void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause) { -/* BUGBUG need cause code for the bridge_channel leaving the bridge. */ if (bridge_channel->state != BRIDGE_CHANNEL_STATE_WAIT) { return; } @@ -133,12 +151,21 @@ void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_ch bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state, new_state); + channel_set_cause(bridge_channel->chan, cause); + /* Change the state on the bridge channel */ bridge_channel->state = new_state; bridge_channel_poke(bridge_channel); } +void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel, enum bridge_channel_state new_state, int cause) +{ + ast_bridge_channel_lock(bridge_channel); + ast_bridge_channel_leave_bridge_nolock(bridge_channel, new_state, cause); + ast_bridge_channel_unlock(bridge_channel); +} + struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel) { struct ast_bridge *bridge = bridge_channel->bridge; @@ -263,12 +290,19 @@ void ast_bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_ch } } -void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel) +void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int cause) { struct ast_bridge_features *features = bridge_channel->features; struct ast_bridge_hook *hook; struct ao2_iterator iter; + ast_bridge_channel_lock(bridge_channel); + if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { + channel_set_cause(bridge_channel->chan, cause); + cause = 0; + } + ast_bridge_channel_unlock(bridge_channel); + /* Run any hangup hooks. */ iter = ao2_iterator_init(features->other_hooks, 0); for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { @@ -287,7 +321,7 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel) ao2_iterator_destroy(&iter); /* Default hangup action. */ - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, cause); } /*! @@ -595,7 +629,7 @@ void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const } if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) { /* Break the bridge if the app returns non-zero. */ - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING); } if (moh_class) { ast_bridge_channel_write_unhold(bridge_channel); @@ -1106,7 +1140,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel, co * here if the hook did not already change the state. */ if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) { - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, 0); } } else if (features->dtmf_passthrough) { /* Stream any collected DTMF to the other channels. */ @@ -1219,7 +1253,7 @@ static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_chan struct blind_transfer_data *blind_data) { ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1); - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING); } /*! @@ -1235,7 +1269,7 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c chan_target = ast_channel_get_by_name(target_chan_name); if (!chan_target) { /* Dang, it disappeared somehow */ - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING); return; } @@ -1252,7 +1286,7 @@ static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_c /* Release the ref we tried to pass to ast_bridge_set_after_callback(). */ ast_channel_unref(chan_target); } - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, AST_CAUSE_NORMAL_CLEARING); } /*! @@ -1337,7 +1371,7 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan if (!bridge->num_channels && ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) { /* Last channel leaving the bridge turns off the lights. */ - bridge_dissolve(bridge); + bridge_dissolve(bridge, ast_channel_hangupcause(bridge_channel->chan)); return; } @@ -1348,7 +1382,7 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan || (bridge_channel->features->usable && ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) { - bridge_dissolve(bridge); + bridge_dissolve(bridge, ast_channel_hangupcause(bridge_channel->chan)); return; } break; @@ -1357,9 +1391,14 @@ static void bridge_channel_dissolve_check(struct ast_bridge_channel *bridge_chan } if (bridge->num_lonely && bridge->num_lonely == bridge->num_channels) { - /* This will start a chain reaction where each channel leaving enters this function and causes - * the next to leave as long as there aren't non-lonely channels in the bridge. */ - ast_bridge_channel_leave_bridge(AST_LIST_FIRST(&bridge->channels), BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + /* + * This will start a chain reaction where each channel leaving + * enters this function and causes the next to leave as long as + * there aren't non-lonely channels in the bridge. + */ + ast_bridge_channel_leave_bridge(AST_LIST_FIRST(&bridge->channels), + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, + ast_channel_hangupcause(bridge_channel->chan)); } } @@ -1473,7 +1512,7 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel) ast_bridge_publish_enter(bridge, bridge_channel->chan, swap ? swap->chan : NULL); if (swap) { - ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0); bridge_channel_internal_pull(swap); } @@ -1671,14 +1710,14 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) } if (!frame) { - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, 0); return; } switch (frame->frametype) { case AST_FRAME_CONTROL: switch (frame->subclass.integer) { case AST_CONTROL_HANGUP: - ast_bridge_channel_kick(bridge_channel); + ast_bridge_channel_kick(bridge_channel, 0); ast_frfree(frame); return; /* BUGBUG This is where incoming HOLD/UNHOLD memory should register. Write UNHOLD into bridge when this channel is pulled. */ @@ -1885,7 +1924,8 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) } if (bridge_channel_internal_push(bridge_channel)) { - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause); res = -1; } bridge_reconfigured(bridge_channel->bridge, 1); diff --git a/main/features.c b/main/features.c index f3e2f0fb16b396175977c2b0db24a8a9258b4e7d..32fccf7a6a7dacb187211da1cf013df881861ff3 100644 --- a/main/features.c +++ b/main/features.c @@ -676,7 +676,7 @@ int ast_bridge_call_with_flags(struct ast_channel *chan, struct ast_channel *pee /* Put peer into the bridge */ if (ast_bridge_impart(bridge, peer, NULL, peer_features, 1)) { - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); ast_bridge_features_cleanup(&chan_features); bridge_failed_peer_goto(chan, peer); return -1; @@ -829,7 +829,7 @@ static int action_bridge(struct mansession *s, const struct message *m) 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); - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); return 0; } @@ -837,7 +837,7 @@ static int action_bridge(struct mansession *s, const struct message *m) 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); - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); return 0; } @@ -1125,7 +1125,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE), xfer_cfg ? xfer_cfg->xfersound : NULL)) { ast_bridge_features_destroy(peer_features); ast_bridge_features_cleanup(&chan_features); - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); goto done; } diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c index 29f38b4b8fc03c2b5b886d8aa09aff7ddff5420c..e34b403e2d074e1a5d001f576b70175a6582680d 100644 --- a/res/parking/parking_applications.c +++ b/res/parking/parking_applications.c @@ -618,14 +618,14 @@ static int parked_call_app_exec(struct ast_channel *chan, const char *data) /* Move the parkee into the new bridge */ if (ast_bridge_move(retrieval_bridge, lot->parking_bridge, pu->chan, NULL, 0)) { - ast_bridge_destroy(retrieval_bridge); + ast_bridge_destroy(retrieval_bridge, 0); return -1; } /* Initialize our bridge features */ res = ast_bridge_features_init(&chan_features); if (res) { - ast_bridge_destroy(retrieval_bridge); + ast_bridge_destroy(retrieval_bridge, 0); ast_bridge_features_cleanup(&chan_features); return -1; } diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c index 5ad0538c5932d8539a8c81e4897a371aa2a57a3b..1f0237b9ed838078d1362da8b2c4f611de4b7b27 100644 --- a/res/parking/parking_bridge_features.c +++ b/res/parking/parking_bridge_features.c @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stasis.h" #include "asterisk/module.h" #include "asterisk/core_local.h" +#include "asterisk/causes.h" struct parked_subscription_datastore { struct stasis_subscription *parked_subscription; @@ -483,7 +484,8 @@ static int parking_duration_callback(struct ast_bridge_channel *bridge_channel, user->resolution = PARK_TIMEOUT; ao2_unlock(user); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, + AST_CAUSE_NORMAL_CLEARING); /* Set parking timeout channel variables */ snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space); @@ -571,14 +573,17 @@ void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *pa if (sscanf(payload, "%u %u", &hangup_after, &numeric_value) != 2) { /* If say_parking_space is called with a non-numeric string, we have a problem. */ ast_assert(0); - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); return; } - ast_say_digits(bridge_channel->chan, numeric_value, "", ast_channel_language(bridge_channel->chan)); + ast_say_digits(bridge_channel->chan, numeric_value, "", + ast_channel_language(bridge_channel->chan)); if (hangup_after) { - ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE); + ast_bridge_channel_leave_bridge(bridge_channel, + BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING); } } diff --git a/res/res_parking.c b/res/res_parking.c index 33cc5607198ce0dcf50d48341b08b858338ba035..12893dba2d7e8b0f94887bdd9d3ec99efe2cfd2a 100644 --- a/res/res_parking.c +++ b/res/res_parking.c @@ -580,7 +580,7 @@ static void parking_lot_destructor(void *obj) struct parking_lot *lot = obj; if (lot->parking_bridge) { - ast_bridge_destroy(lot->parking_bridge); + ast_bridge_destroy(lot->parking_bridge, 0); } ao2_cleanup(lot->parked_users); ao2_cleanup(lot->cfg); diff --git a/res/res_stasis.c b/res/res_stasis.c index 39f6be1347fd3ff822ee1fddca8809a7ab6a4b34..963e70d9ebe672af38746eb22150b44fc7882158 100644 --- a/res/res_stasis.c +++ b/res/res_stasis.c @@ -471,7 +471,7 @@ void stasis_app_bridge_destroy(const char *bridge_id) return; } ao2_unlink(app_bridges, bridge); - ast_bridge_destroy(bridge); + ast_bridge_destroy(bridge, 0); } int app_send_start_msg(struct app *app, struct ast_channel *chan,