Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
A
asterisk
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Issue analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Voice
asterisk
Commits
895dbe53
Commit
895dbe53
authored
9 years ago
by
Joshua Colp
Committed by
Gerrit Code Review
9 years ago
Browse files
Options
Downloads
Plain Diff
Merge "chan_sip: Fix early call pickup caused deadlock." into 11
parents
6d911576
0e6d3f5e
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
channels/chan_sip.c
+96
-34
96 additions, 34 deletions
channels/chan_sip.c
with
96 additions
and
34 deletions
channels/chan_sip.c
+
96
−
34
View file @
895dbe53
...
...
@@ -25121,6 +25121,39 @@ static int sip_t38_abort(const void *data)
return 0;
}
/*! \brief Checks state of the p->refer->refer_call state: can it be picked up?
*
* It will terminate the call if it cannot be picked up; e.g. because it
* was gone, or because it wasn't in a ringing state.
*
* \return 1 if terminated, 0 if ok
*/
static int terminate_on_invalid_replaces_state(struct sip_pvt *p, struct sip_request *req, const char *replace_id)
{
if (p->refer->refer_call == p) {
ast_log(LOG_NOTICE, "INVITE with replaces into its own call id (%s == %s)!\n", replace_id, p->callid);
transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
} else if (!p->refer->refer_call->owner) {
/* Oops, someting wrong anyway, no owner, no call */
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
/* Check for better return code */
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req);
} else if (ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RINGING &&
ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RING &&
ast_channel_state(p->refer->refer_call->owner) != AST_STATE_UP) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
transmit_response_reliable(p, "603 Declined (Replaces)", req);
} else {
/* Ok */
return 0;
}
/* Terminated */
return 1;
}
/*!
* \brief bare-bones support for SIP UPDATE
*
...
...
@@ -25322,6 +25355,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
int gotdest;
const char *p_replaces;
char *replace_id = NULL;
int magic_call_id = 0;
int refer_locked = 0;
const char *required;
unsigned int required_profile = 0;
...
...
@@ -25553,40 +25587,27 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
ast_channel_unlock(subscription->owner);
}
subscription = dialog_unref(subscription, "unref dialog subscription");
magic_call_id = !ast_strlen_zero(pickup.exten);
}
}
/* This locks both refer_call pvt and refer_call pvt's owner!!!*/
if (!error && ast_strlen_zero(pickup.exten) && (p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
error = 1;
} else {
refer_locked = 1;
}
/* The matched call is the call from the transferer to Asterisk .
We want to bridge the bridged part of the call to the
incoming invite, thus taking over the refered call */
if (p->refer->refer_call == p) {
ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid);
transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */
error = 1;
}
if (!error && ast_strlen_zero(pickup.exten) && !p->refer->refer_call->owner) {
/* Oops, someting wrong anyway, no owner, no call */
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id);
/* Check for better return code */
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req);
error = 1;
}
if (!error && ast_strlen_zero(pickup.exten) && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RINGING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_RING && ast_channel_state(p->refer->refer_call->owner) != AST_STATE_UP) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id);
transmit_response_reliable(p, "603 Declined (Replaces)", req);
error = 1;
if (!error) {
if (magic_call_id) {
;
/* This locks both refer_call pvt and refer_call pvt's owner!!!*/
} else if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag))) {
/* The matched call is the call from the transferer to Asterisk .
We want to bridge the bridged part of the call to the
incoming invite, thus taking over the refered call */
refer_locked = 1;
if (terminate_on_invalid_replaces_state(p, req, replace_id)) {
error = 1;
}
} else {
ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id);
transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req);
error = 1;
}
}
if (error) { /* Give up this dialog */
...
...
@@ -25828,11 +25849,52 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
goto request_invite_cleanup;
}
/* We cannot call sip_new with any channel locks
* held. Unlock the referred channel if locked. */
if (refer_locked) {
sip_pvt_unlock(p->refer->refer_call);
if (p->refer->refer_call->owner) {
ast_channel_unlock(p->refer->refer_call->owner);
}
}
/* First invitation - create the channel. Allocation
* failures are handled below. */
c = sip_new(p, AST_STATE_DOWN, S_OR(p->peername, NULL), NULL, p->logger_callid);
/* Reacquire the lock on the referred call. */
if (refer_locked) {
/* Reuse deadlock avoid pattern found in
* get_sip_pvt_byid_locked. */
sip_pvt_lock(p->refer->refer_call);
while (p->refer->refer_call->owner && ast_channel_trylock(p->refer->refer_call->owner)) {
sip_pvt_unlock(p->refer->refer_call);
usleep(1);
sip_pvt_lock(p->refer->refer_call);
}
/* And now, check once more that the call is still
* still available. Yuck. Bail out if it isn't. */
if (terminate_on_invalid_replaces_state(p, req, replace_id)) {
/* Free the referred call: it's not ours anymore. */
sip_pvt_unlock(p->refer->refer_call);
if (p->refer->refer_call->owner) {
ast_channel_unlock(p->refer->refer_call->owner);
}
p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
refer_locked = 0;
/* Kill the channel we just created. */
sip_pvt_unlock(p);
ast_hangup(c);
sip_pvt_lock(p); /* pvt is expected to remain locked on return, so re-lock it */
/* Mark the call as failed. */
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
p->invitestate = INV_COMPLETED;
res = INV_REQ_ERROR;
goto request_invite_cleanup;
}
}
if (cc_recall_core_id != -1) {
ast_setup_cc_recall_datastore(c, cc_recall_core_id);
ast_cc_agent_set_interfaces_chanvar(c);
...
...
@@ -25891,7 +25953,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
p->lastinvite = seqno;
if (c && replace_id) { /* Attended transfer or call pickup - we're the target */
if (
!ast_strlen_zero(pickup.exten)
) {
if (
magic_call_id
) {
append_history(p, "Xfer", "INVITE/Replace received");
/* Let the caller know we're giving it a shot */
...
...
@@ -26062,7 +26124,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
request_invite_cleanup:
if (refer_locked
&& p->refer && p->refer->refer_call
) {
if (refer_locked) {
sip_pvt_unlock(p->refer->refer_call);
if (p->refer->refer_call->owner) {
ast_channel_unlock(p->refer->refer_call->owner);
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment