summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2012-06-29 17:02:32 +0000
committerRichard Mudgett <rmudgett@digium.com>2012-06-29 17:02:32 +0000
commitac35b92b628064a1fcf962895f0befd57aff0442 (patch)
tree0f2a4fb769643d0ae24dfed49468d80e8652aabf /main
parent35c533156cf94b005bd7c561d69e40dc5e837dd6 (diff)
Hangup handlers - Dialplan subroutines that run when the channel hangs up.
Hangup handlers are an alternative to the h extension. They can be used in addition to the h extension. The idea is to attach a Gosub routine to a channel that will execute when the call hangs up. Whereas which h extension gets executed depends on the location of dialplan execution when the call hangs up, hangup handlers are attached to the call channel. You can attach multiple handlers that will execute in the order of most recently added first. (closes issue ASTERISK-19549) Reported by: Mark Murawski Tested by: rmudgett Review: https://reviewboard.asterisk.org/r/2002/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@369493 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/autoservice.c10
-rw-r--r--main/channel.c93
-rw-r--r--main/channel_internal_api.c5
-rw-r--r--main/features.c139
-rw-r--r--main/pbx.c382
5 files changed, 481 insertions, 148 deletions
diff --git a/main/autoservice.c b/main/autoservice.c
index 37bbee682..afbcbd3e0 100644
--- a/main/autoservice.c
+++ b/main/autoservice.c
@@ -313,6 +313,16 @@ int ast_autoservice_stop(struct ast_channel *chan)
return res;
}
+void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
+{
+ if (chan && !ast_autoservice_start(chan)) {
+ ast_hangup(peer);
+ ast_autoservice_stop(chan);
+ } else {
+ ast_hangup(peer);
+ }
+}
+
int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype)
{
struct asent *as;
diff --git a/main/channel.c b/main/channel.c
index 59d40dc6c..0f4c3bc77 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -1107,6 +1107,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
headp = ast_channel_varshead(tmp);
AST_LIST_HEAD_INIT_NOLOCK(headp);
+ ast_pbx_hangup_handler_init(tmp);
AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp));
AST_LIST_HEAD_INIT_NOLOCK(ast_channel_autochans(tmp));
@@ -1183,6 +1184,9 @@ struct ast_channel *ast_dummy_channel_alloc(void)
return NULL;
}
+ ast_pbx_hangup_handler_init(tmp);
+ AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp));
+
headp = ast_channel_varshead(tmp);
AST_LIST_HEAD_INIT_NOLOCK(headp);
@@ -2198,8 +2202,11 @@ static void ast_channel_destructor(void *obj)
ast_cel_check_retire_linkedid(chan);
}
- /* Get rid of each of the data stores on the channel */
+ ast_pbx_hangup_handler_destroy(chan);
+
ast_channel_lock(chan);
+
+ /* Get rid of each of the data stores on the channel */
while ((datastore = AST_LIST_REMOVE_HEAD(ast_channel_datastores(chan), entry)))
/* Free the data store */
ast_datastore_free(datastore);
@@ -2316,10 +2323,17 @@ static void ast_channel_destructor(void *obj)
static void ast_dummy_channel_destructor(void *obj)
{
struct ast_channel *chan = obj;
+ struct ast_datastore *datastore;
struct ast_var_t *vardata;
struct varshead *headp;
- headp = ast_channel_varshead(chan);
+ ast_pbx_hangup_handler_destroy(chan);
+
+ /* Get rid of each of the data stores on the channel */
+ while ((datastore = AST_LIST_REMOVE_HEAD(ast_channel_datastores(chan), entry))) {
+ /* Free the data store */
+ ast_datastore_free(datastore);
+ }
ast_party_dialed_free(ast_channel_dialed(chan));
ast_party_caller_free(ast_channel_caller(chan));
@@ -2328,6 +2342,7 @@ static void ast_dummy_channel_destructor(void *obj)
/* loop over the variables list, freeing all data and deleting list items */
/* no need to lock the list, as the channel is already locked */
+ headp = ast_channel_varshead(chan);
while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
ast_var_delete(vardata);
@@ -2599,7 +2614,6 @@ static void destroy_hooks(struct ast_channel *chan)
int ast_hangup(struct ast_channel *chan)
{
char extra_str[64]; /* used for cel logging below */
- int was_zombie;
ast_autoservice_stop(chan);
@@ -2632,11 +2646,20 @@ int ast_hangup(struct ast_channel *chan)
}
/* Mark as a zombie so a masquerade cannot be setup on this channel. */
- if (!(was_zombie = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE))) {
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE);
- }
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE);
ast_channel_unlock(chan);
+
+ /*
+ * XXX if running the hangup handlers here causes problems
+ * because the handlers take too long to execute, we could move
+ * the meat of this function into another thread. A thread
+ * where channels go to die.
+ *
+ * If this is done, ast_autoservice_chan_hangup_peer() will no
+ * longer be needed.
+ */
+ ast_pbx_hangup_handler_run(chan);
ao2_unlink(channels, chan);
ast_channel_lock(chan);
@@ -2675,14 +2698,10 @@ int ast_hangup(struct ast_channel *chan)
(long) pthread_self(), ast_channel_name(chan), (long)ast_channel_blocker(chan), ast_channel_blockproc(chan));
ast_assert(ast_test_flag(ast_channel_flags(chan), AST_FLAG_BLOCKING) == 0);
}
- if (!was_zombie) {
- ast_debug(1, "Hanging up channel '%s'\n", ast_channel_name(chan));
- if (ast_channel_tech(chan)->hangup) {
- ast_channel_tech(chan)->hangup(chan);
- }
- } else {
- ast_debug(1, "Hanging up zombie '%s'\n", ast_channel_name(chan));
+ ast_debug(1, "Hanging up channel '%s'\n", ast_channel_name(chan));
+ if (ast_channel_tech(chan)->hangup) {
+ ast_channel_tech(chan)->hangup(chan);
}
ast_channel_unlock(chan);
@@ -6535,6 +6554,7 @@ int ast_do_masquerade(struct ast_channel *original)
const struct ast_channel_tech *t;
void *t_pvt;
union {
+ struct ast_hangup_handler_list handlers;
struct ast_party_dialed dialed;
struct ast_party_caller caller;
struct ast_party_connected_line connected;
@@ -6758,6 +6778,11 @@ int ast_do_masquerade(struct ast_channel *original)
ast_app_group_update(clonechan, original);
+ /* Swap hangup handlers. */
+ exchange.handlers = *ast_channel_hangup_handlers(original);
+ *ast_channel_hangup_handlers(original) = *ast_channel_hangup_handlers(clonechan);
+ *ast_channel_hangup_handlers(clonechan) = exchange.handlers;
+
/* Move data stores over */
if (AST_LIST_FIRST(ast_channel_datastores(clonechan))) {
struct ast_datastore *ds;
@@ -6878,19 +6903,6 @@ int ast_do_masquerade(struct ast_channel *original)
* container lock.
*/
ast_channel_unlock(original);
-
- /* Disconnect the original original's physical side */
- if (ast_channel_tech(clonechan)->hangup && ast_channel_tech(clonechan)->hangup(clonechan)) {
- ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n");
- } else {
- /*
- * We just hung up the original original's physical side of the
- * channel. Set the new zombie to use the kill channel driver
- * for safety.
- */
- ast_channel_tech_set(clonechan, &ast_kill_tech);
- }
-
ast_channel_unlock(clonechan);
/*
@@ -6938,33 +6950,18 @@ int ast_do_masquerade(struct ast_channel *original)
ast_datastore_free(xfer_ds);
}
- if (clone_was_zombie) {
- ast_channel_lock(clonechan);
- ast_debug(1, "Destroying channel clone '%s'\n", ast_channel_name(clonechan));
- ast_manager_event(clonechan, EVENT_FLAG_CALL, "Hangup",
- "Channel: %s\r\n"
- "Uniqueid: %s\r\n"
- "Cause: %d\r\n"
- "Cause-txt: %s\r\n",
- ast_channel_name(clonechan),
- ast_channel_uniqueid(clonechan),
- ast_channel_hangupcause(clonechan),
- ast_cause2str(ast_channel_hangupcause(clonechan))
- );
- ast_channel_unlock(clonechan);
-
- /*
- * Drop the system reference to destroy the channel since it is
- * already unlinked.
- */
- ast_channel_unref(clonechan);
- } else {
+ if (!clone_was_zombie) {
ao2_link(channels, clonechan);
}
-
ao2_link(channels, original);
ao2_unlock(channels);
+ if (clone_was_zombie) {
+ /* Restart the ast_hangup() that was deferred because of this masquerade. */
+ ast_debug(1, "Destroying channel clone '%s'\n", ast_channel_name(clonechan));
+ ast_hangup(clonechan);
+ }
+
/* Release our held safety references. */
ast_channel_unref(original);
ast_channel_unref(clonechan);
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index 1922db9a9..b91e2e253 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -137,6 +137,7 @@ struct ast_channel {
struct ast_readq_list readq;
struct ast_jb jb; /*!< The jitterbuffer state */
struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
+ struct ast_hangup_handler_list hangup_handlers;/*!< Hangup handlers on the channel. */
struct ast_datastore_list datastores; /*!< Data stores on the channel */
struct ast_autochan_list autochans; /*!< Autochans on the channel */
unsigned long insmpl; /*!< Track the read/written samples for monitor use */
@@ -884,6 +885,10 @@ struct ast_format *ast_channel_writeformat(struct ast_channel *chan)
{
return &chan->writeformat;
}
+struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan)
+{
+ return &chan->hangup_handlers;
+}
struct ast_datastore_list *ast_channel_datastores(struct ast_channel *chan)
{
return &chan->datastores;
diff --git a/main/features.c b/main/features.c
index 79d1036f2..b602dbb98 100644
--- a/main/features.c
+++ b/main/features.c
@@ -1077,10 +1077,10 @@ static void *bridge_call_thread(void *data)
ast_log(LOG_VERBOSE, "putting peer %s into PBX again\n", ast_channel_name(tobj->peer));
if (ast_pbx_start(tobj->peer)) {
ast_log(LOG_WARNING, "FAILED continuing PBX on peer %s\n", ast_channel_name(tobj->peer));
- ast_hangup(tobj->peer);
+ ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer);
}
} else {
- ast_hangup(tobj->peer);
+ ast_autoservice_chan_hangup_peer(tobj->chan, tobj->peer);
}
if (!ast_check_hangup(tobj->chan)) {
ast_log(LOG_VERBOSE, "putting chan %s into PBX again\n", ast_channel_name(tobj->chan));
@@ -2526,7 +2526,7 @@ static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
if (ast_channel_make_compatible(c, newchan) < 0) {
ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
ast_channel_name(c), ast_channel_name(newchan));
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(c, newchan);
return -1;
}
return 0;
@@ -2762,7 +2762,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
}
if (ast_check_hangup(newchan) || !ast_check_hangup(transferer)) {
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferer, newchan);
if (ast_stream_and_wait(transferer, xfersound, "")) {
ast_log(LOG_WARNING, "Failed to play transfer sound!\n");
}
@@ -2882,7 +2882,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
/* newchan is up, we should prepare transferee and bridge them */
if (ast_check_hangup(newchan)) {
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferee, newchan);
ast_party_connected_line_free(&connected_line);
return -1;
}
@@ -2908,7 +2908,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
xferchan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", ast_channel_linkedid(transferee), 0, "Transfered/%s", ast_channel_name(transferee));
if (!xferchan) {
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferee, newchan);
ast_party_connected_line_free(&connected_line);
return -1;
}
@@ -2922,7 +2922,7 @@ static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, st
if (ast_channel_masquerade(xferchan, transferee)) {
ast_hangup(xferchan);
- ast_hangup(newchan);
+ ast_autoservice_chan_hangup_peer(transferee, newchan);
ast_party_connected_line_free(&connected_line);
return -1;
}
@@ -4169,7 +4169,6 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
int diff;
int hasfeatures=0;
int hadfeatures=0;
- int autoloopflag;
int sendingdtmfdigit = 0;
int we_disabled_peer_cdr = 0;
struct ast_option_header *aoh;
@@ -4179,7 +4178,8 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
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 */
struct ast_silence_generator *silgen = NULL;
- const char *h_context;
+ /*! TRUE if h-exten or hangup handlers run. */
+ int hangup_run = 0;
pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer));
pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan));
@@ -4611,105 +4611,82 @@ before_you_go:
config->end_bridge_callback(config->end_bridge_callback_data);
}
- /* run the hangup exten on the chan object IFF it was NOT involved in a parking situation
- * if it were, then chan belongs to a different thread now, and might have been hung up long
- * ago.
- */
- if (ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) {
- h_context = NULL;
- } else if (ast_exists_extension(chan, ast_channel_context(chan), "h", 1,
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
- h_context = ast_channel_context(chan);
- } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
- && ast_exists_extension(chan, ast_channel_macrocontext(chan), "h", 1,
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
- h_context = ast_channel_macrocontext(chan);
- } else {
- h_context = NULL;
- }
- if (h_context) {
+ if (!ast_test_flag(&config->features_caller, AST_FEATURE_NO_H_EXTEN)) {
struct ast_cdr *swapper = NULL;
char savelastapp[AST_MAX_EXTENSION];
char savelastdata[AST_MAX_EXTENSION];
char save_context[AST_MAX_CONTEXT];
char save_exten[AST_MAX_EXTENSION];
int save_prio;
- int found = 0; /* set if we find at least one match */
- int spawn_error = 0;
- /*
- * Make sure that the channel is marked as hungup since we are
- * going to run the "h" exten on it.
- */
- ast_softhangup(chan, AST_SOFTHANGUP_APPUNLOAD);
-
- autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
- if (bridge_cdr && ast_opt_end_cdr_before_h_exten) {
- ast_cdr_end(bridge_cdr);
- }
-
- /* swap the bridge cdr and the chan cdr for a moment, and let the endbridge
- dialplan code operate on it */
ast_channel_lock(chan);
if (bridge_cdr) {
+ /*
+ * Swap the bridge_cdr and the chan cdr for a moment, and let
+ * the hangup dialplan code operate on it.
+ */
swapper = ast_channel_cdr(chan);
+ ast_channel_cdr_set(chan, bridge_cdr);
+
+ /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
ast_copy_string(savelastapp, bridge_cdr->lastapp, sizeof(bridge_cdr->lastapp));
ast_copy_string(savelastdata, bridge_cdr->lastdata, sizeof(bridge_cdr->lastdata));
- ast_channel_cdr_set(chan, bridge_cdr);
}
ast_copy_string(save_context, ast_channel_context(chan), sizeof(save_context));
ast_copy_string(save_exten, ast_channel_exten(chan), sizeof(save_exten));
save_prio = ast_channel_priority(chan);
- if (h_context != ast_channel_context(chan)) {
- ast_channel_context_set(chan, h_context);
- }
- ast_channel_exten_set(chan, "h");
- ast_channel_priority_set(chan, 1);
ast_channel_unlock(chan);
- while ((spawn_error = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
- ast_channel_priority(chan),
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
- &found, 1)) == 0) {
- ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
- }
- if (found && spawn_error) {
- /* Something bad happened, or a hangup has been requested. */
- ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan));
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan), ast_channel_name(chan));
- }
+ ast_autoservice_start(peer);
+ if (ast_exists_extension(chan, ast_channel_context(chan), "h", 1,
+ S_COR(ast_channel_caller(chan)->id.number.valid,
+ ast_channel_caller(chan)->id.number.str, NULL))) {
+ ast_pbx_h_exten_run(chan, ast_channel_context(chan));
+ hangup_run = 1;
+ } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
+ && ast_exists_extension(chan, ast_channel_macrocontext(chan), "h", 1,
+ S_COR(ast_channel_caller(chan)->id.number.valid,
+ ast_channel_caller(chan)->id.number.str, NULL))) {
+ ast_pbx_h_exten_run(chan, ast_channel_macrocontext(chan));
+ hangup_run = 1;
+ }
+ if (ast_pbx_hangup_handler_run(chan)) {
+ /* Indicate hangup handlers were run. */
+ hangup_run = 1;
+ }
+ ast_autoservice_stop(peer);
- /* swap it back */
ast_channel_lock(chan);
+
+ /* swap it back */
ast_channel_context_set(chan, save_context);
ast_channel_exten_set(chan, save_exten);
ast_channel_priority_set(chan, save_prio);
if (bridge_cdr) {
if (ast_channel_cdr(chan) == bridge_cdr) {
ast_channel_cdr_set(chan, swapper);
+
+ /* Restore the lastapp/lastdata */
+ ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
+ ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
} else {
bridge_cdr = NULL;
}
}
- /* An "h" exten has been run, so indicate that one has been run. */
- ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
ast_channel_unlock(chan);
-
- /* protect the lastapp/lastdata against the effects of the hangup/dialplan code */
- if (bridge_cdr) {
- ast_copy_string(bridge_cdr->lastapp, savelastapp, sizeof(bridge_cdr->lastapp));
- ast_copy_string(bridge_cdr->lastdata, savelastdata, sizeof(bridge_cdr->lastdata));
- }
- ast_set2_flag(ast_channel_flags(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... */
new_chan_cdr = pick_unlocked_cdr(ast_channel_cdr(chan)); /* the proper chan cdr, if there are forked cdrs */
- /* If the channel CDR has been modified during the call, record the changes in the bridge cdr,
- * BUT, if we've gone through the h extension block above, the CDR got swapped so don't overwrite
- * what was done in the h extension. What a mess. This is why you never touch CDR code. */
- if (new_chan_cdr && bridge_cdr && !h_context) {
+
+ /*
+ * If the channel CDR has been modified during the call, record
+ * the changes in the bridge cdr, BUT, if hangup_run, the CDR
+ * got swapped so don't overwrite what was done in the
+ * h-extension or hangup handlers. What a mess. This is why
+ * you never touch CDR code.
+ */
+ if (new_chan_cdr && bridge_cdr && !hangup_run) {
ast_cdr_copy_vars(bridge_cdr, new_chan_cdr);
ast_copy_string(bridge_cdr->userfield, new_chan_cdr->userfield, sizeof(bridge_cdr->userfield));
bridge_cdr->amaflags = new_chan_cdr->amaflags;
@@ -5540,7 +5517,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
break;
}
if (res) {
- ast_hangup(peer);
+ ast_autoservice_chan_hangup_peer(chan, peer);
parkinglot_unref(parkinglot);
return -1;
}
@@ -5549,7 +5526,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
res = ast_channel_make_compatible(chan, peer);
if (res < 0) {
ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(peer));
- ast_hangup(peer);
+ ast_autoservice_chan_hangup_peer(chan, peer);
parkinglot_unref(parkinglot);
return -1;
}
@@ -5601,7 +5578,7 @@ static int parked_call_exec(struct ast_channel *chan, const char *data)
ast_cdr_setdestchan(ast_channel_cdr(chan), ast_channel_name(peer));
/* Simulate the PBX hanging up */
- ast_hangup(peer);
+ ast_autoservice_chan_hangup_peer(chan, peer);
} else {
if (ast_stream_and_wait(chan, "pbx-invalidpark", "")) {
ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark",
@@ -8014,7 +7991,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
"Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan));
/* Maybe we should return this channel to the PBX? */
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE");
current_dest_chan = ast_channel_unref(current_dest_chan);
@@ -8088,7 +8065,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
goto_opt = ast_goto_if_exists(final_dest_chan, caller_context, caller_extension, caller_priority + 1);
}
if (goto_opt || ast_pbx_start(final_dest_chan)) {
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
}
} else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
@@ -8097,16 +8074,16 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
if (ast_pbx_start(final_dest_chan)) {
ast_log(LOG_WARNING, "FAILED continuing PBX on dest chan %s\n", ast_channel_name(final_dest_chan));
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
} else {
ast_debug(1, "SUCCESS continuing PBX on chan %s\n", ast_channel_name(final_dest_chan));
}
} else {
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
}
} else {
ast_debug(1, "chan %s was hungup\n", ast_channel_name(final_dest_chan));
- ast_hangup(final_dest_chan);
+ ast_autoservice_chan_hangup_peer(chan, final_dest_chan);
}
done:
ast_free((char *) bconfig.warning_sound);
diff --git a/main/pbx.c b/main/pbx.c
index 006549c12..db929519e 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -5342,6 +5342,360 @@ int ast_spawn_extension(struct ast_channel *c, const char *context, const char *
return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
}
+void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
+{
+ int autoloopflag;
+ int found;
+ int spawn_error;
+
+ ast_channel_lock(chan);
+
+ if (ast_channel_cdr(chan) && ast_opt_end_cdr_before_h_exten) {
+ ast_cdr_end(ast_channel_cdr(chan));
+ }
+
+ /* Set h exten location */
+ if (context != ast_channel_context(chan)) {
+ ast_channel_context_set(chan, context);
+ }
+ ast_channel_exten_set(chan, "h");
+ ast_channel_priority_set(chan, 1);
+
+ /*
+ * Make sure that the channel is marked as hungup since we are
+ * going to run the h exten on it.
+ */
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD);
+
+ /* Save autoloop flag */
+ autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
+ ast_channel_unlock(chan);
+
+ for (;;) {
+ spawn_error = ast_spawn_extension(chan, ast_channel_context(chan),
+ ast_channel_exten(chan), ast_channel_priority(chan),
+ S_COR(ast_channel_caller(chan)->id.number.valid,
+ ast_channel_caller(chan)->id.number.str, NULL), &found, 1);
+
+ ast_channel_lock(chan);
+ if (spawn_error) {
+ /* The code after the loop needs the channel locked. */
+ break;
+ }
+ ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
+ ast_channel_unlock(chan);
+ }
+ if (found && spawn_error) {
+ /* Something bad happened, or a hangup has been requested. */
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n",
+ ast_channel_context(chan), ast_channel_exten(chan),
+ ast_channel_priority(chan), ast_channel_name(chan));
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
+ ast_channel_context(chan), ast_channel_exten(chan),
+ ast_channel_priority(chan), ast_channel_name(chan));
+ }
+
+ /* An "h" exten has been run, so indicate that one has been run. */
+ ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN);
+
+ /* Restore autoloop flag */
+ ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
+ ast_channel_unlock(chan);
+}
+
+int ast_pbx_hangup_handler_run(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+
+ ast_channel_lock(chan);
+ handlers = ast_channel_hangup_handlers(chan);
+ if (AST_LIST_EMPTY(handlers)) {
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ if (ast_channel_cdr(chan) && ast_opt_end_cdr_before_h_exten) {
+ ast_cdr_end(ast_channel_cdr(chan));
+ }
+
+ /*
+ * Make sure that the channel is marked as hungup since we are
+ * going to run the hangup handlers on it.
+ */
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD);
+
+ for (;;) {
+ handlers = ast_channel_hangup_handlers(chan);
+ h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
+ if (!h_handler) {
+ break;
+ }
+
+ /*** DOCUMENTATION
+ <managerEventInstance>
+ <synopsis>Raised when a hangup handler is about to be called.</synopsis>
+ <syntax>
+ <parameter name="Handler">
+ <para>Hangup handler parameter string passed to the Gosub application.</para>
+ </parameter>
+ </syntax>
+ </managerEventInstance>
+ ***/
+ manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerRun",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Handler: %s\r\n",
+ ast_channel_name(chan),
+ ast_channel_uniqueid(chan),
+ h_handler->args);
+ ast_channel_unlock(chan);
+
+ ast_app_exec_sub(NULL, chan, h_handler->args, 1);
+ ast_free(h_handler);
+
+ ast_channel_lock(chan);
+ }
+ ast_channel_unlock(chan);
+ return 1;
+}
+
+void ast_pbx_hangup_handler_init(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+
+ handlers = ast_channel_hangup_handlers(chan);
+ AST_LIST_HEAD_INIT_NOLOCK(handlers);
+}
+
+void ast_pbx_hangup_handler_destroy(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+
+ ast_channel_lock(chan);
+
+ /* Get rid of each of the hangup handlers on the channel */
+ handlers = ast_channel_hangup_handlers(chan);
+ while ((h_handler = AST_LIST_REMOVE_HEAD(handlers, node))) {
+ ast_free(h_handler);
+ }
+
+ ast_channel_unlock(chan);
+}
+
+int ast_pbx_hangup_handler_pop(struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+
+ ast_channel_lock(chan);
+ handlers = ast_channel_hangup_handlers(chan);
+ h_handler = AST_LIST_REMOVE_HEAD(handlers, node);
+ if (h_handler) {
+ /*** DOCUMENTATION
+ <managerEventInstance>
+ <synopsis>
+ Raised when a hangup handler is removed from the handler
+ stack by the CHANNEL() function.
+ </synopsis>
+ <syntax>
+ <parameter name="Handler">
+ <para>Hangup handler parameter string passed to the Gosub application.</para>
+ </parameter>
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">HangupHandlerPush</ref>
+ <ref type="function">CHANNEL</ref>
+ </see-also>
+ </managerEventInstance>
+ ***/
+ manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerPop",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Handler: %s\r\n",
+ ast_channel_name(chan),
+ ast_channel_uniqueid(chan),
+ h_handler->args);
+ }
+ ast_channel_unlock(chan);
+ if (h_handler) {
+ ast_free(h_handler);
+ return 1;
+ }
+ return 0;
+}
+
+void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+ const char *expanded_handler;
+
+ if (ast_strlen_zero(handler)) {
+ return;
+ }
+
+ expanded_handler = ast_app_expand_sub_args(chan, handler);
+ if (!expanded_handler) {
+ return;
+ }
+ h_handler = ast_malloc(sizeof(*h_handler) + 1 + strlen(expanded_handler));
+ if (!h_handler) {
+ ast_free((char *) expanded_handler);
+ return;
+ }
+ strcpy(h_handler->args, expanded_handler);/* Safe */
+ ast_free((char *) expanded_handler);
+
+ ast_channel_lock(chan);
+
+ handlers = ast_channel_hangup_handlers(chan);
+ AST_LIST_INSERT_HEAD(handlers, h_handler, node);
+
+ /*** DOCUMENTATION
+ <managerEventInstance>
+ <synopsis>
+ Raised when a hangup handler is added to the handler
+ stack by the CHANNEL() function.
+ </synopsis>
+ <syntax>
+ <parameter name="Handler">
+ <para>Hangup handler parameter string passed to the Gosub application.</para>
+ </parameter>
+ </syntax>
+ <see-also>
+ <ref type="managerEvent">HangupHandlerPop</ref>
+ <ref type="function">CHANNEL</ref>
+ </see-also>
+ </managerEventInstance>
+ ***/
+ manager_event(EVENT_FLAG_DIALPLAN, "HangupHandlerPush",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Handler: %s\r\n",
+ ast_channel_name(chan),
+ ast_channel_uniqueid(chan),
+ h_handler->args);
+
+ ast_channel_unlock(chan);
+}
+
+#define HANDLER_FORMAT "%-30s %s\n"
+
+/*!
+ * \internal
+ * \brief CLI output the hangup handler headers.
+ * \since 11.0
+ *
+ * \param fd CLI file descriptor to use.
+ *
+ * \return Nothing
+ */
+static void ast_pbx_hangup_handler_headers(int fd)
+{
+ ast_cli(fd, HANDLER_FORMAT, "Channel", "Handler");
+}
+
+/*!
+ * \internal
+ * \brief CLI output the channel hangup handlers.
+ * \since 11.0
+ *
+ * \param fd CLI file descriptor to use.
+ * \param chan Channel to show hangup handlers.
+ *
+ * \return Nothing
+ */
+static void ast_pbx_hangup_handler_show(int fd, struct ast_channel *chan)
+{
+ struct ast_hangup_handler_list *handlers;
+ struct ast_hangup_handler *h_handler;
+ int first = 1;
+
+ ast_channel_lock(chan);
+ handlers = ast_channel_hangup_handlers(chan);
+ AST_LIST_TRAVERSE(handlers, h_handler, node) {
+ ast_cli(fd, HANDLER_FORMAT, first ? ast_channel_name(chan) : "", h_handler->args);
+ first = 0;
+ }
+ ast_channel_unlock(chan);
+}
+
+/*
+ * \brief 'show hanguphandlers <channel>' CLI command implementation function...
+ */
+static char *handle_show_hangup_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hanguphandlers";
+ e->usage =
+ "Usage: core show hanguphandlers <channel>\n"
+ " Show hangup handlers of a specified channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
+ }
+
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+
+ chan = ast_channel_get_by_name(a->argv[3]);
+ if (!chan) {
+ ast_cli(a->fd, "Channel does not exist.\n");
+ return CLI_FAILURE;
+ }
+
+ ast_pbx_hangup_handler_headers(a->fd);
+ ast_pbx_hangup_handler_show(a->fd, chan);
+
+ ast_channel_unref(chan);
+
+ return CLI_SUCCESS;
+}
+
+/*
+ * \brief 'show hanguphandlers all' CLI command implementation function...
+ */
+static char *handle_show_hangup_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel_iterator *iter;
+ struct ast_channel *chan;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hanguphandlers all";
+ e->usage =
+ "Usage: core show hanguphandlers all\n"
+ " Show hangup handlers for all channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
+ }
+
+ if (a->argc < 4) {
+ return CLI_SHOWUSAGE;
+ }
+
+ iter = ast_channel_iterator_all_new();
+ if (!iter) {
+ return CLI_FAILURE;
+ }
+
+ ast_pbx_hangup_handler_headers(a->fd);
+ for (; (chan = ast_channel_iterator_next(iter)); ast_channel_unref(chan)) {
+ ast_pbx_hangup_handler_show(a->fd, chan);
+ }
+ ast_channel_iterator_destroy(iter);
+
+ return CLI_SUCCESS;
+}
+
/*! helper function to set extension and priority */
static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
{
@@ -5680,27 +6034,15 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c,
if (!args || !args->no_hangup_chan) {
ast_softhangup(c, AST_SOFTHANGUP_APPUNLOAD);
- }
-
- if ((!args || !args->no_hangup_chan)
- && !ast_test_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN)
- && ast_exists_extension(c, ast_channel_context(c), "h", 1,
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL))) {
- set_ext_pri(c, "h", 1);
- if (ast_channel_cdr(c) && ast_opt_end_cdr_before_h_exten) {
- ast_cdr_end(ast_channel_cdr(c));
- }
- while ((res = ast_spawn_extension(c, ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c),
- S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL),
- &found, 1)) == 0) {
- ast_channel_priority_set(c, ast_channel_priority(c) + 1);
- }
- if (found && res) {
- /* Something bad happened, or a hangup has been requested. */
- ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
- ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_channel_name(c));
+ if (!ast_test_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN)
+ && ast_exists_extension(c, ast_channel_context(c), "h", 1,
+ S_COR(ast_channel_caller(c)->id.number.valid,
+ ast_channel_caller(c)->id.number.str, NULL))) {
+ ast_pbx_h_exten_run(c, ast_channel_context(c));
}
+ ast_pbx_hangup_handler_run(c);
}
+
ast_set2_flag(ast_channel_flags(c), autoloopflag, AST_FLAG_IN_AUTOLOOP);
ast_clear_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */
pbx_destroy(ast_channel_pbx(c));
@@ -7590,6 +7932,8 @@ static struct ast_cli_entry pbx_cli[] = {
#endif
AST_CLI_DEFINE(handle_show_chanvar, "Show channel variables"),
AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
+ AST_CLI_DEFINE(handle_show_hangup_all, "Show hangup handlers of all channels"),
+ AST_CLI_DEFINE(handle_show_hangup_channel, "Show hangup handlers of a specified channel"),
AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"),
AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),