This merges the masqpark branch into 1.4

These changes eliminate the need for (and use of)
the KEEPALIVE return code in res_features.c;
There are other places that use this result code
for similar purposes at a higher level, these appear
to be left alone in 1.4, but attacked in trunk.

The reason these changes are being made in 1.4, is
that parking ends a channel's life, in some situations,
and the code in the bridge (and some other places),
was not checking the result code properly, and dereferencing
the channel pointer, which could lead to memory corruption
and crashes.

Calling the masq_park function eliminates this danger 
in higher levels.

A series of previous commits have replaced some parking calls
with masq_park, but this patch puts them ALL to rest,
(except one, purposely left alone because a masquerade
is done anyway), and gets rid of the code that tests
the KEEPALIVE result, and the NOHANGUP_PEER result codes.

While bug 13820 inspired this work, this patch does
not solve all the problems mentioned there.

I have tested this patch (again) to make sure I have
not introduced regressions. 

Crashes that occurred when a parked party hung up
while the parking party was listening to the numbers
of the parking stall being assigned, is eliminated.

These are the cases where parking code may be activated:

1. Feature one touch (eg. *3)
2. Feature blind xfer to parking lot (eg ##700)
3. Run Park() app from dialplan (eg sip xfer to 700)
   (eg. dahdi hookflash xfer to 700)
4. Run Park via manager.

The interesting testing cases for parking are:
I. A calls B, A parks B
    a. B hangs up while A is getting the numbers announced.
    b. B hangs up after A gets the announcement, but 
       before the parking time expires
    c. B waits, time expires, A is redialed,
       A answers, B and A are connected, after
       which, B hangs up.
    d. C picks up B while still in parking lot.

II. A calls B, B parks A
    a. A hangs up while B is getting the numbers announced.
    b. A hangs up after B gets the announcement, but 
       before the parking time expires
    c. A waits, time expires, B is redialed,
       B answers, A and B are connected, after
       which, A hangs up.
    d. C picks up A while still in parking lot.

Testing this throroughly involves acting all the permutations
of I and II, in situations 1,2,3, and 4.

Since I added a few more changes (ALL references to KEEPALIVE in the bridge
code eliimated (I missed one earlier), I retested
most of the above cases, and no crashes.

H-extension weirdness.

Current h-extension execution is not completely
correct for several of the cases.

For the case where A calls B, and A parks B, the
'h' exten is run on A's channel as soon as the park
is accomplished. This is expected behavior.

But when A calls B, and B parks A, this will be
current behavior:

After B parks A, B is hung up by the system, and
the 'h' (hangup) exten gets run, but the channel
mentioned will be a derivative of A's...

Thus, if A is DAHDI/1, and B is DAHDI/2,
the h-extension will be run on channel
Parked/DAHDI/1-1<ZOMBIE>, and the 
start/answer/end info will be those 
relating to Channel A.

And, in the case where A is reconnected to
B after the park time expires, when both parties
hang up after the joyful reunion, no h-exten
will be run at all.

In the case where C picks up A from the 
parking lot, when either A or C hang up,
the h-exten will be run for the C channel.

CDR's are a separate issue, and not addressed
here.

As to WHY this strange behavior occurs, 
the answer lies in the procedure followed
to accomplish handing over the channel
to the parking manager thread. This procedure
is called masquerading. In the process,
a duplicate copy of the channel is created,
and most of the active data is given to the
new copy. The original channel gets its name
changed to XXX<ZOMBIE> and keeps the PBX
information for the sake of the original
thread (preserving its role as a call 
originator, if it had this role to begin
with), while the new channel is without
this info and becomes a call target (a
"peer").

In this case, the parking lot manager
thread is handed the new (masqueraded)
channel. It will not run an h-exten
on the channel if it hangs up while
in the parking lot. The h exten will
be run on the original channel instead,
in the original thread, after the bridge
completes.

See bug 13820 for our intentions as
to how to clean up the h exten behavior.

Review: http://reviewboard.digium.com/r/29/



git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@166093 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Steve Murphy 17 years ago
parent e0bec5d67d
commit e3700a13a4

@ -1837,37 +1837,29 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
res = -1;
}
if (res != AST_PBX_NO_HANGUP_PEER && res != AST_PBX_NO_HANGUP_PEER_PARKED) {
if (res != AST_PBX_KEEPALIVE && !chan->_softhangup)
chan->hangupcause = peer->hangupcause;
ast_hangup(peer);
}
if (!chan->_softhangup)
chan->hangupcause = peer->hangupcause;
ast_hangup(peer);
}
out:
/* cleaning up chan is not a good idea here if AST_PBX_KEEPALIVE
is returned; chan will get the love it needs from another
thread */
if (res != AST_PBX_KEEPALIVE) {
if (moh) {
moh = 0;
ast_moh_stop(chan);
} else if (sentringing) {
sentringing = 0;
ast_indicate(chan, -1);
}
ast_rtp_early_bridge(chan, NULL);
hanguptree(outgoing, NULL);
pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
if (option_debug)
ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status);
if ((ast_test_flag(peerflags, OPT_GO_ON)) && (!chan->_softhangup) && (res != AST_PBX_KEEPALIVE)) {
if (timelimit)
chan->whentohangup = 0;
res = 0;
}
if (moh) {
moh = 0;
ast_moh_stop(chan);
} else if (sentringing) {
sentringing = 0;
ast_indicate(chan, -1);
}
ast_rtp_early_bridge(chan, NULL);
hanguptree(outgoing, NULL);
pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
if (option_debug)
ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status);
if (ast_test_flag(peerflags, OPT_GO_ON) && !chan->_softhangup) {
if (calldurationlimit)
chan->whentohangup = 0;
res = 0;
}
done:
ast_module_user_remove(u);
return res;

@ -3155,7 +3155,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
if (bridge != AST_PBX_KEEPALIVE && !attended_transfer_occurred(qe->chan)) {
if (!attended_transfer_occurred(qe->chan)) {
struct ast_datastore *tds;
if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
@ -3164,7 +3164,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
} else if (qe->chan->_softhangup) {
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED && qe->parent->eventwhencalled)
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
@ -3181,7 +3181,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
} else {
ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
(long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED && qe->parent->eventwhencalled)
if (qe->parent->eventwhencalled)
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
@ -3206,8 +3206,7 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
if (transfer_ds) {
ast_channel_datastore_free(transfer_ds);
}
if (bridge != AST_PBX_NO_HANGUP_PEER && bridge != AST_PBX_NO_HANGUP_PEER_PARKED)
ast_hangup(peer);
ast_hangup(peer);
res = bridge ? bridge : 1;
ao2_ref(member, -1);
}
@ -4079,7 +4078,7 @@ stop:
}
/* Don't allow return code > 0 */
if (res >= 0 && res != AST_PBX_KEEPALIVE) {
if (res >= 0) {
res = 0;
if (ringing) {
ast_indicate(chan, -1);

@ -38,8 +38,6 @@ extern "C" {
/*! \brief Special return values from applications to the PBX { */
#define AST_PBX_KEEPALIVE 10 /*!< Destroy the thread, but don't hang up the channel */
#define AST_PBX_NO_HANGUP_PEER 11 /*!< The peer has been involved in a transfer */
#define AST_PBX_NO_HANGUP_PEER_PARKED 12 /*!< Don't touch the peer channel - it was sent to the parking lot and might be gone by now */
/*! } */
#define PRIORITY_HINT -1 /*!< Special Priority for a hint */

@ -535,12 +535,8 @@ static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel
{
return masq_park_call(rchan, peer, timeout, extout, 1);
}
#define FEATURE_RETURN_HANGUP -1
#define FEATURE_RETURN_SUCCESSBREAK 0
#define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
#define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
#define FEATURE_RETURN_NO_HANGUP_PEER_PARKED AST_PBX_NO_HANGUP_PEER_PARKED
#define FEATURE_RETURN_PASSDIGITS 21
#define FEATURE_RETURN_STOREDIGITS 22
#define FEATURE_RETURN_SUCCESS 23
@ -575,30 +571,22 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer,
u = ast_module_user_add(chan);
set_peers(&parker, &parkee, peer, chan, sense);
/* Setup the exten/priority to be s/1 since we don't know
where this call should return */
strcpy(chan->exten, "s");
chan->priority = 1;
/* we used to set chan's exten and priority to "s" and 1
here, but this generates (in some cases) an invalid
extension, and if "s" exists, could errantly
cause execution of extensions you don't expect It
makes more sense to let nature take its course
when chan finishes, and let the pbx do its thing
and hang up when the park is over.
*/
if (chan->_state != AST_STATE_UP)
res = ast_answer(chan);
if (!res)
res = ast_safe_sleep(chan, 1000);
if (!res) {
if (sense == FEATURE_SENSE_CHAN) {
res = ast_park_call(parkee, parker, 0, NULL);
if (!res) {
if (sense == FEATURE_SENSE_CHAN) {
res = AST_PBX_NO_HANGUP_PEER_PARKED;
} else {
res = AST_PBX_KEEPALIVE;
}
}
}
else if (sense == FEATURE_SENSE_PEER) {
masq_park_call_announce(parkee, parker, 0, NULL);
res = 0; /* PBX should hangup zombie channel */
}
if (!res) { /* one direction used to call park_call.... */
masq_park_call_announce(parkee, parker, 0, NULL);
res = 0; /* PBX should hangup zombie channel */
}
ast_module_user_remove(u);
@ -1118,17 +1106,7 @@ static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer,
ast_autoservice_stop(idle);
if (res == AST_PBX_KEEPALIVE) {
/* do not hangup peer if feature is to be activated on it */
if ((ast_test_flag(feature, AST_FEATURE_FLAG_ONPEER) && sense == FEATURE_SENSE_CHAN) || (ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF) && sense == FEATURE_SENSE_PEER)) {
return FEATURE_RETURN_NO_HANGUP_PEER;
} else
return FEATURE_RETURN_PBX_KEEPALIVE;
} else if (res == AST_PBX_NO_HANGUP_PEER) {
return FEATURE_RETURN_NO_HANGUP_PEER;
} else if (res == AST_PBX_NO_HANGUP_PEER_PARKED) {
return FEATURE_RETURN_NO_HANGUP_PEER_PARKED;
} else if (res)
if (res)
return FEATURE_RETURN_SUCCESSBREAK;
return FEATURE_RETURN_SUCCESS; /*! \todo XXX should probably return res */
@ -1729,13 +1707,13 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
}
before_you_go:
if (res != AST_PBX_KEEPALIVE && config->end_bridge_callback) {
if (config->end_bridge_callback) {
config->end_bridge_callback(config->end_bridge_callback_data);
}
autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
if (res != AST_PBX_KEEPALIVE && !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 = NULL;
char savelastapp[AST_MAX_EXTENSION];
char savelastdata[AST_MAX_EXTENSION];
@ -1787,10 +1765,9 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
/* obey the NoCDR() wishes. -- move the DISABLED flag to the bridge CDR if it was set on the channel during the bridge... */
if (res != AST_PBX_KEEPALIVE) {
new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
if (bridge_cdr && new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED))
ast_set_flag(bridge_cdr, AST_CDR_FLAG_POST_DISABLED);
new_chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
if (bridge_cdr && new_chan_cdr && ast_test_flag(new_chan_cdr, AST_CDR_FLAG_POST_DISABLED)) {
ast_set_flag(bridge_cdr, AST_CDR_FLAG_POST_DISABLED);
}
/* we can post the bridge CDR at this point */
@ -1821,23 +1798,9 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
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.
6. Parking has to be accounted for in the code:
a. Parking will cause ast_bridge_call to return
either AST_PBX_NO_HANGUP_PEER or AST_PBX_NO_HANGUP_PEER_PARKED;
in the latter case, peer is (most likely) a bad
pointer, you can no longer deref it. If it does still
exist, it is under another's thread control, and
could be destroyed at any time.
b. The same applies to AST_PBX_KEEPALIVE, in which
case, the chan ptr cannot be used, as another thread
owns it and may have destroyed the channel.
c. In the former case, you need to check peer to see if it
still exists before you deref it, and obtain a lock.
d. In neither case should you do an ast_hangup(peer).
e. Do not overwrite the result code from ast_bridge_call.
*/
if (res != AST_PBX_KEEPALIVE && new_chan_cdr) {
if (new_chan_cdr) {
struct ast_channel *chan_ptr = NULL;
if (strcasecmp(orig_channame, chan->name) != 0) {
@ -1863,7 +1826,7 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
}
}
if (res != AST_PBX_NO_HANGUP_PEER_PARKED) { /* if the peer was involved in a park, don't even touch it; it's probably gone */
{
struct ast_channel *chan_ptr = NULL;
new_peer_cdr = pick_unlocked_cdr(peer->cdr); /* the proper chan cdr, if there are forked cdrs */
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))
@ -2121,14 +2084,19 @@ static int park_call_exec(struct ast_channel *chan, void *data)
res = ast_safe_sleep(chan, 1000);
/* Park the call */
if (!res) {
res = park_call_full(chan, chan, 0, NULL, orig_chan_name);
res = park_call_full(chan, chan, 0, NULL, orig_chan_name); /* In experiments, using the masq_park_call
func here yielded no difference with
current implementation. I saw no advantage
in calling it instead.
*/
/* Continue on in the dialplan */
if (res == 1) {
ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
chan->priority = orig_priority;
res = 0;
} else if (!res)
res = AST_PBX_KEEPALIVE;
} else if (!res) {
res = 1;
}
}
ast_module_user_remove(u);
@ -2261,8 +2229,7 @@ static int park_exec(struct ast_channel *chan, void *data)
ast_cdr_setdestchan(chan->cdr, peer->name);
/* Simulate the PBX hanging up */
if (res != AST_PBX_NO_HANGUP_PEER && res != AST_PBX_NO_HANGUP_PEER_PARKED)
ast_hangup(peer);
ast_hangup(peer);
ast_module_user_remove(u);
return res;
} else {

Loading…
Cancel
Save