Merged revisions 144066 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r144066 | murf | 2008-09-23 10:41:49 -0600 (Tue, 23 Sep 2008) | 29 lines (closes issue #13489) Reported by: DougUDI Tested by: murf (closes issue #13490) Reported by: seanbright Tested by: murf (closes issue #13467) Reported by: edantie Tested by: murf, edantie, DougUDI This crash happens because we are unsafely handling old pointers. The channel whose cdr is being handled, has been hung up and destroyed already. I reorganized the code a bit, and tried not to lose the fork-cdr-chain concepts of the previous code. I now verify that the 'previous' channel (the channel we had when the bridge was started), still exists, by looking it up by name in the channel list. I also do not try to reset the CDR's of channels involved in bridges. Testing shows it solves the crash problem, and should not negatively impact previous fixes involving CDR's generated during/after blind transfers. (The reason we need to reset the CDR's on the "beginning" channels in the first place). ........ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@144067 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
57b96b0814
commit
281ea9e4f7
1 changed files with 76 additions and 43 deletions
103
main/features.c
103
main/features.c
|
@ -2099,6 +2099,8 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
||||||
struct ast_cdr *orig_peer_cdr = NULL;
|
struct ast_cdr *orig_peer_cdr = NULL;
|
||||||
struct ast_cdr *chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
|
struct ast_cdr *chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
|
||||||
struct ast_cdr *peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
|
struct ast_cdr *peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
|
||||||
|
struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
|
||||||
|
struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
|
||||||
|
|
||||||
memset(&backup_config, 0, sizeof(backup_config));
|
memset(&backup_config, 0, sizeof(backup_config));
|
||||||
|
|
||||||
|
@ -2351,6 +2353,9 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
||||||
|
|
||||||
}
|
}
|
||||||
before_you_go:
|
before_you_go:
|
||||||
|
new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
|
||||||
|
new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
|
||||||
|
|
||||||
if (!ast_test_flag(&(config->features_caller),AST_FEATURE_NO_H_EXTEN) && ast_exists_extension(chan, chan->context, "h", 1, chan->cid.cid_num)) {
|
if (!ast_test_flag(&(config->features_caller),AST_FEATURE_NO_H_EXTEN) && ast_exists_extension(chan, chan->context, "h", 1, chan->cid.cid_num)) {
|
||||||
struct ast_cdr *swapper;
|
struct ast_cdr *swapper;
|
||||||
char savelastapp[AST_MAX_EXTENSION];
|
char savelastapp[AST_MAX_EXTENSION];
|
||||||
|
@ -2359,7 +2364,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
||||||
int save_prio;
|
int save_prio;
|
||||||
int found = 0; /* set if we find at least one match */
|
int found = 0; /* set if we find at least one match */
|
||||||
|
|
||||||
if (chan->cdr && ast_opt_end_cdr_before_h_exten) {
|
if (ast_opt_end_cdr_before_h_exten) {
|
||||||
ast_cdr_end(bridge_cdr);
|
ast_cdr_end(bridge_cdr);
|
||||||
}
|
}
|
||||||
/* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
|
/* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
|
||||||
|
@ -2396,52 +2401,80 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
||||||
}
|
}
|
||||||
|
|
||||||
/* obey the NoCDR() wishes. */
|
/* obey the NoCDR() wishes. */
|
||||||
if (chan_cdr && ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED) && peer_cdr && !ast_test_flag(peer_cdr, AST_CDR_FLAG_POST_DISABLED))
|
if (new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED) && new_peer_cdr && !ast_test_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED))
|
||||||
ast_set_flag(peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
|
ast_set_flag(new_peer_cdr, AST_CDR_FLAG_POST_DISABLED); /* DISABLED is viral-- it will propagate across a bridge */
|
||||||
if (!chan_cdr || (chan_cdr && !ast_test_flag(chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
|
if (!new_chan_cdr || (new_chan_cdr && !ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED))) {
|
||||||
|
struct ast_channel *chan_ptr = NULL;
|
||||||
|
|
||||||
ast_cdr_end(bridge_cdr);
|
ast_cdr_end(bridge_cdr);
|
||||||
|
|
||||||
ast_cdr_detach(bridge_cdr);
|
ast_cdr_detach(bridge_cdr);
|
||||||
|
|
||||||
/* just in case, these channels get bridged again before hangup */
|
/* do a specialized reset on the beginning channel
|
||||||
if (chan_cdr) {
|
CDR's, if they still exist, so as not to mess up
|
||||||
ast_cdr_specialized_reset(chan_cdr,0);
|
issues in future bridges;
|
||||||
}
|
|
||||||
if (peer_cdr) {
|
|
||||||
struct ast_cdr *cur;
|
|
||||||
|
|
||||||
ast_channel_lock(peer);
|
Here are the rules of the game:
|
||||||
for (cur = peer_cdr; cur; cur = cur->next) {
|
1. The chan and peer channel pointers will not change
|
||||||
if (cur == orig_peer_cdr) {
|
during the life of the bridge.
|
||||||
|
2. But, in transfers, the channel names will change.
|
||||||
|
between the time the bridge is started, and the
|
||||||
|
time the channel ends.
|
||||||
|
Usually, when a channel changes names, it will
|
||||||
|
also change CDR pointers.
|
||||||
|
3. Usually, only one of the two channels (chan or peer)
|
||||||
|
will change names.
|
||||||
|
4. Usually, if a channel changes names during a bridge,
|
||||||
|
it is because of a transfer. Usually, in these situations,
|
||||||
|
it is normal to see 2 bridges running simultaneously, and
|
||||||
|
it is not unusual to see the two channels that change
|
||||||
|
swapped between bridges.
|
||||||
|
5. After a bridge occurs, we have 2 or 3 channels' CDRs
|
||||||
|
to attend to; if the chan or peer changed names,
|
||||||
|
we have the before and after attached CDR's.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (strcasecmp(orig_channame, chan->name) != 0) {
|
||||||
|
/* old channel */
|
||||||
|
chan_ptr = ast_get_channel_by_name_locked(orig_channame);
|
||||||
|
if (chan_ptr) {
|
||||||
|
if (!ast_bridged_channel(chan_ptr)) {
|
||||||
|
struct ast_cdr *cur;
|
||||||
|
for (cur = chan_ptr->cdr; cur; cur = cur->next) {
|
||||||
|
if (cur == chan_cdr) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cur)
|
||||||
if (!cur) {
|
ast_cdr_specialized_reset(chan_cdr,0);
|
||||||
/* orig_peer_cdr is gone, probably because of a masquerade
|
|
||||||
* during the bridge. */
|
|
||||||
ast_channel_unlock(peer);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
ast_channel_unlock(chan_ptr);
|
||||||
/* before resetting the peer cdr, throw a copy of it to the
|
}
|
||||||
backend, just in case the cdr.conf file is calling for
|
/* new channel */
|
||||||
unanswered CDR's. */
|
ast_cdr_specialized_reset(new_chan_cdr,0);
|
||||||
|
} else {
|
||||||
/* When peer_cdr isn't the same addr as orig_peer_cdr,
|
ast_cdr_specialized_reset(chan_cdr,0); /* nothing changed, reset the chan_cdr */
|
||||||
this can only happen if there was a transfer, methinks;
|
}
|
||||||
at any rate, only pay attention to the original*/
|
if (strcasecmp(orig_peername, peer->name) != 0) {
|
||||||
if (ast_cdr_isset_unanswered()) {
|
/* old channel */
|
||||||
struct ast_cdr *dupd = ast_cdr_dup(orig_peer_cdr);
|
chan_ptr = ast_get_channel_by_name_locked(orig_peername);
|
||||||
if (dupd) {
|
if (chan_ptr) {
|
||||||
if (ast_tvzero(dupd->end) && ast_cdr_isset_unanswered())
|
if (!ast_bridged_channel(chan_ptr)) {
|
||||||
ast_cdr_end(dupd);
|
struct ast_cdr *cur;
|
||||||
ast_cdr_detach(dupd);
|
for (cur = chan_ptr->cdr; cur; cur = cur->next) {
|
||||||
|
if (cur == peer_cdr) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast_cdr_specialized_reset(orig_peer_cdr,0);
|
if (cur)
|
||||||
ast_channel_unlock(peer);
|
ast_cdr_specialized_reset(peer_cdr,0);
|
||||||
|
}
|
||||||
|
ast_channel_unlock(chan_ptr);
|
||||||
|
}
|
||||||
|
/* new channel */
|
||||||
|
ast_cdr_specialized_reset(new_peer_cdr,0);
|
||||||
|
} else {
|
||||||
|
ast_cdr_specialized_reset(peer_cdr,0); /* nothing changed, reset the peer_cdr */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
Reference in a new issue