#include "asterisk/astobj2.h"
#include "asterisk/stringfields.h"
#include "asterisk/stasis_channels.h"
+#include "asterisk/causes.h"
/*** DOCUMENTATION
<application name="AgentLogin" language="en_US">
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) {
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);
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);
* 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;
}
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);
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 (;;) {
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",
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
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);
}
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);
}
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");
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");
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");
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");
/* 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();
#include "asterisk/bridge.h"
#include "asterisk/musiconhold.h"
#include "asterisk/astobj2.h"
+#include "asterisk/causes.h"
/*** DOCUMENTATION
<application name="BridgeWait" language="en_US">
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);
}
}
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;
}
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;
}
/* 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;
}
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);
}
#include "asterisk/monitor.h"
#include "asterisk/mixmonitor.h"
#include "asterisk/audiohook.h"
+#include "asterisk/causes.h"
enum set_touch_variables_res {
SET_TOUCH_SUCCESS,
* 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;
}
#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)
{
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;
}
* \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. */
* \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
* 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.
*
* \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.
* \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)
}
* \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
*
* \return Nothing
*/
-void bridge_dissolve(struct ast_bridge *bridge);
+void bridge_dissolve(struct ast_bridge *bridge, int cause);
#endif /* _ASTERISK_PRIVATE_BRIDGING_H */
#include "asterisk/pbx.h"
#include "asterisk/test.h"
#include "asterisk/_private.h"
-
#include "asterisk/heap.h"
#include "asterisk/say.h"
#include "asterisk/timing.h"
#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;
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 = {
}
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. */
&& 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
* 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;
}
}
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;
}
}
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);
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);
* 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",
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);
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)
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;
}
}
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;
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);
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;
}
}
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) {
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;
}
ast_cli(a->fd, "Destroying bridge '%s'\n", a->argv[2]);
- ast_bridge_destroy(bridge);
+ ast_bridge_destroy(bridge, 0);
return CLI_SUCCESS;
}
}
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;
}
if (props->target_bridge) {
- ast_bridge_destroy(props->target_bridge);
+ ast_bridge_destroy(props->target_bridge, 0);
props->target_bridge = NULL;
}
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;
#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.
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
}
}
-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;
}
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;
}
}
-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)) {
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);
}
/*!
}
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);
* 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. */
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);
}
/*!
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;
}
/* 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);
}
/*!
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;
}
|| (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;
}
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));
}
}
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);
}
}
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. */
}
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);
/* 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;
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;
}
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;
}
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;
}
/* 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;
}
#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;
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);
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);
}
}
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);
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,