Merge "bridge.c: Crash during attended transfer when missing a local channel half"
This commit is contained in:
commit
66d7c1c86f
3 changed files with 85 additions and 8 deletions
|
@ -41,6 +41,38 @@ struct stasis_message_type;
|
||||||
|
|
||||||
/* ------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Lock the "chan" and "owner" channels (and return them) on the base
|
||||||
|
* private structure as well as the base private structure itself.
|
||||||
|
*
|
||||||
|
* \note This also adds references to each of the above mentioned elements and
|
||||||
|
* also the underlying private local structure.
|
||||||
|
* \note None of these locks should be held prior to calling this function.
|
||||||
|
* \note To undo this process call ast_local_unlock_all.
|
||||||
|
*
|
||||||
|
* \since 13.8.0
|
||||||
|
*
|
||||||
|
* \param chan Must be a local channel
|
||||||
|
* \param outchan The local channel's "chan" channel
|
||||||
|
* \param outowner The local channel's "owner" channel
|
||||||
|
*/
|
||||||
|
void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan,
|
||||||
|
struct ast_channel **outowner);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Unlock the "chan" and "owner" channels on the base private structure
|
||||||
|
* as well as the base private structure itself.
|
||||||
|
*
|
||||||
|
* \note This also removes references to each of the above mentioned elements and
|
||||||
|
* also the underlying private local structure.
|
||||||
|
* \note This function should be used in conjunction with ast_local_lock_all.
|
||||||
|
*
|
||||||
|
* \since 13.8.0
|
||||||
|
*
|
||||||
|
* \param chan Must be a local channel
|
||||||
|
*/
|
||||||
|
void ast_local_unlock_all(struct ast_channel *chan);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Get the other local channel in the pair.
|
* \brief Get the other local channel in the pair.
|
||||||
* \since 12.0.0
|
* \since 12.0.0
|
||||||
|
|
|
@ -4034,19 +4034,25 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
|
||||||
BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
|
BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
|
||||||
|
|
||||||
if (bridge2) {
|
if (bridge2) {
|
||||||
RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup);
|
|
||||||
struct ast_channel *locals[2];
|
struct ast_channel *locals[2];
|
||||||
|
|
||||||
ast_channel_lock(local_chan);
|
/* Have to lock everything just in case a hangup comes in early */
|
||||||
local_chan2 = ast_local_get_peer(local_chan);
|
ast_local_lock_all(local_chan, &locals[0], &locals[1]);
|
||||||
ast_channel_unlock(local_chan);
|
if (!locals[0] || !locals[1]) {
|
||||||
|
ast_log(LOG_ERROR, "Transfer failed probably due to an early hangup - "
|
||||||
|
"missing other half of '%s'\n", ast_channel_name(local_chan));
|
||||||
|
ast_local_unlock_all(local_chan);
|
||||||
|
ao2_cleanup(local_chan);
|
||||||
|
return AST_BRIDGE_TRANSFER_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
ast_assert(local_chan2 != NULL);
|
/* Make sure the peer is properly set */
|
||||||
|
if (local_chan != locals[0]) {
|
||||||
locals[0] = local_chan;
|
SWAP(locals[0], locals[1]);
|
||||||
locals[1] = local_chan2;
|
}
|
||||||
|
|
||||||
ast_attended_transfer_message_add_link(transfer_msg, locals);
|
ast_attended_transfer_message_add_link(transfer_msg, locals);
|
||||||
|
ast_local_unlock_all(local_chan);
|
||||||
} else {
|
} else {
|
||||||
ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
|
ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,6 +235,45 @@ struct local_pvt {
|
||||||
char exten[AST_MAX_EXTENSION];
|
char exten[AST_MAX_EXTENSION];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan,
|
||||||
|
struct ast_channel **outowner)
|
||||||
|
{
|
||||||
|
struct local_pvt *p = ast_channel_tech_pvt(chan);
|
||||||
|
|
||||||
|
*outchan = NULL;
|
||||||
|
*outowner = NULL;
|
||||||
|
|
||||||
|
if (p) {
|
||||||
|
ao2_ref(p, 1);
|
||||||
|
ast_unreal_lock_all(&p->base, outchan, outowner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_local_unlock_all(struct ast_channel *chan)
|
||||||
|
{
|
||||||
|
struct local_pvt *p = ast_channel_tech_pvt(chan);
|
||||||
|
struct ast_unreal_pvt *base;
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base = &p->base;
|
||||||
|
|
||||||
|
if (base->owner) {
|
||||||
|
ast_channel_unlock(base->owner);
|
||||||
|
ast_channel_unref(base->owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base->chan) {
|
||||||
|
ast_channel_unlock(base->chan);
|
||||||
|
ast_channel_unref(base->chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
ao2_unlock(base);
|
||||||
|
ao2_ref(p, -1);
|
||||||
|
}
|
||||||
|
|
||||||
struct ast_channel *ast_local_get_peer(struct ast_channel *ast)
|
struct ast_channel *ast_local_get_peer(struct ast_channel *ast)
|
||||||
{
|
{
|
||||||
struct local_pvt *p = ast_channel_tech_pvt(ast);
|
struct local_pvt *p = ast_channel_tech_pvt(ast);
|
||||||
|
|
Loading…
Reference in a new issue