summaryrefslogtreecommitdiff
path: root/main/channel.c
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/channel.c
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/channel.c')
-rw-r--r--main/channel.c93
1 files changed, 45 insertions, 48 deletions
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);