[asterisk-commits] rmudgett: trunk r369493 - in /trunk: ./ apps/ channels/ configs/ funcs/ inclu...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jun 29 12:02:38 CDT 2012


Author: rmudgett
Date: Fri Jun 29 12:02:32 2012
New Revision: 369493

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=369493
Log:
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/

Modified:
    trunk/CHANGES
    trunk/apps/app_dial.c
    trunk/apps/app_followme.c
    trunk/apps/app_queue.c
    trunk/channels/chan_local.c
    trunk/configs/cdr.conf.sample
    trunk/funcs/func_channel.c
    trunk/include/asterisk/channel.h
    trunk/include/asterisk/pbx.h
    trunk/main/autoservice.c
    trunk/main/channel.c
    trunk/main/channel_internal_api.c
    trunk/main/features.c
    trunk/main/pbx.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Jun 29 12:02:32 2012
@@ -46,6 +46,9 @@
    have their own verbosity level.  The command 'core set verbose' will now set
    a separate level for each remote console without affecting any other
    console.
+ * Hangup handlers can be attached to channels using the CHANNEL(hangup_handler_xxx)
+   options.  Hangup handlers will run when the channel is hung up similar to the
+   h extension.
 
 CLI Changes
 -------------------

Modified: trunk/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_dial.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/apps/app_dial.c (original)
+++ trunk/apps/app_dial.c Fri Jun 29 12:02:32 2012
@@ -1881,7 +1881,8 @@
 		}
 		return 0; /* the good exit path */
 	} else {
-		ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */
+		/* hang up on the callee -- he didn't want to talk anyway! */
+		ast_autoservice_chan_hangup_peer(chan, peer);
 		return -1;
 	}
 }
@@ -2774,7 +2775,7 @@
 				if (active_chan) {
 					struct ast_frame *fr = ast_read(active_chan);
 					if (!fr) {
-						ast_hangup(peer);
+						ast_autoservice_chan_hangup_peer(chan, peer);
 						res = -1;
 						goto done;
 					}
@@ -2790,7 +2791,7 @@
 							switch (fr->subclass.integer) {
 								case AST_CONTROL_HANGUP:
 									ast_frfree(fr);
-									ast_hangup(peer);
+									ast_autoservice_chan_hangup_peer(chan, peer);
 									res = -1;
 									goto done;
 								default:
@@ -2821,7 +2822,7 @@
 			ast_channel_exten_set(peer, ast_channel_exten(chan));
 			ast_channel_priority_set(peer, ast_channel_priority(chan) + 2);
 			if (ast_pbx_start(peer)) {
-				ast_hangup(peer);
+				ast_autoservice_chan_hangup_peer(chan, peer);
 			}
 			hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
 			if (continue_exec)
@@ -3027,7 +3028,7 @@
 			res = ast_channel_make_compatible(chan, peer);
 			if (res < 0) {
 				ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(chan), ast_channel_name(peer));
-				ast_hangup(peer);
+				ast_autoservice_chan_hangup_peer(chan, peer);
 				res = -1;
 				goto done;
 			}
@@ -3047,28 +3048,9 @@
 		if (ast_test_flag64(&opts, OPT_PEER_H)
 			&& ast_exists_extension(peer, ast_channel_context(peer), "h", 1,
 				S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
-			int autoloopflag;
-			int found;
-			int res9;
-			
-			ast_channel_exten_set(peer, "h");
-			ast_channel_priority_set(peer, 1);
-			autoloopflag = ast_test_flag(ast_channel_flags(peer), AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
-			ast_set_flag(ast_channel_flags(peer), AST_FLAG_IN_AUTOLOOP);
-
-			while ((res9 = ast_spawn_extension(peer, ast_channel_context(peer), ast_channel_exten(peer),
-				ast_channel_priority(peer),
-				S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL),
-				&found, 1)) == 0) {
-				ast_channel_priority_set(peer, ast_channel_priority(peer) + 1);
-			}
-
-			if (found && res9) {
-				/* 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(peer), ast_channel_exten(peer), ast_channel_priority(peer), ast_channel_name(peer));
-				ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", ast_channel_context(peer), ast_channel_exten(peer), ast_channel_priority(peer), ast_channel_name(peer));
-			}
-			ast_set2_flag(ast_channel_flags(peer), autoloopflag, AST_FLAG_IN_AUTOLOOP);  /* set it back the way it was */
+			ast_autoservice_start(chan);
+			ast_pbx_h_exten_run(peer, ast_channel_context(peer));
+			ast_autoservice_stop(chan);
 		}
 		if (!ast_check_hangup(peer)) {
 			if (ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) {
@@ -3089,7 +3071,7 @@
 				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
 			}
 		}
-		ast_hangup(peer);
+		ast_autoservice_chan_hangup_peer(chan, peer);
 	}
 out:
 	if (moh) {

Modified: trunk/apps/app_followme.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_followme.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/apps/app_followme.c (original)
+++ trunk/apps/app_followme.c Fri Jun 29 12:02:32 2012
@@ -1490,7 +1490,7 @@
 		res = ast_channel_make_compatible(caller, outbound);
 		if (res < 0) {
 			ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(caller), ast_channel_name(outbound));
-			ast_hangup(outbound);
+			ast_autoservice_chan_hangup_peer(caller, outbound);
 			goto outrun;
 		}
 
@@ -1513,7 +1513,7 @@
 		}
 
 		res = ast_bridge_call(caller, outbound, &config);
-		ast_hangup(outbound);
+		ast_autoservice_chan_hangup_peer(caller, outbound);
 	}
 
 outrun:

Modified: trunk/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_queue.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/apps/app_queue.c (original)
+++ trunk/apps/app_queue.c Fri Jun 29 12:02:32 2012
@@ -5266,7 +5266,7 @@
 							"%s",
 							queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
 							qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
-				ast_hangup(peer);
+				ast_autoservice_chan_hangup_peer(qe->chan, peer);
 				ao2_ref(member, -1);
 				goto out;
 			} else if (ast_check_hangup(qe->chan)) {
@@ -5274,7 +5274,7 @@
 				ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
 				ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
 				record_abandoned(qe);
-				ast_hangup(peer);
+				ast_autoservice_chan_hangup_peer(qe->chan, peer);
 				ao2_ref(member, -1);
 				return -1;
 			}
@@ -5296,7 +5296,7 @@
 			ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->chan), ast_channel_name(peer));
 			record_abandoned(qe);
 			ast_cdr_failed(ast_channel_cdr(qe->chan));
-			ast_hangup(peer);
+			ast_autoservice_chan_hangup_peer(qe->chan, peer);
 			ao2_ref(member, -1);
 			return -1;
 		}
@@ -5673,10 +5673,10 @@
 					caller_priority + 1);
 			}
 			if (goto_res || ast_pbx_start(peer)) {
-				ast_hangup(peer);
+				ast_autoservice_chan_hangup_peer(qe->chan, peer);
 			}
 		} else {
-			ast_hangup(peer);
+			ast_autoservice_chan_hangup_peer(qe->chan, peer);
 		}
 
 		res = bridge ? bridge : 1;

Modified: trunk/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_local.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/channels/chan_local.c (original)
+++ trunk/channels/chan_local.c Fri Jun 29 12:02:32 2012
@@ -238,6 +238,12 @@
 		return -1;
 	}
 
+	if (!strcmp(write_info->function, "CHANNEL")
+		&& !strncasecmp(write_info->data, "hangup_handler_", 15)) {
+		/* Block CHANNEL(hangup_handler_xxx) writes to the other local channel. */
+		return 0;
+	}
+
 	/* get the tech pvt */
 	if (!(p = ast_channel_tech_pvt(ast))) {
 		return -1;

Modified: trunk/configs/cdr.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/cdr.conf.sample?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/configs/cdr.conf.sample (original)
+++ trunk/configs/cdr.conf.sample Fri Jun 29 12:02:32 2012
@@ -36,8 +36,9 @@
 
 ; Normally, CDR's are not closed out until after all extensions are finished
 ; executing.  By enabling this option, the CDR will be ended before executing
-; the "h" extension so that CDR values such as "end" and "billsec" may be
-; retrieved inside of of this extension.  The default value is "no".
+; the "h" extension and hangup handlers so that CDR values such as "end" and
+; "billsec" may be retrieved inside of of this extension.
+; The default value is "no".
 ;endbeforehexten=no
 
 ; Normally, the 'billsec' field logged to the backends (text files or databases)

Modified: trunk/funcs/func_channel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/funcs/func_channel.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/funcs/func_channel.c (original)
+++ trunk/funcs/func_channel.c Fri Jun 29 12:02:32 2012
@@ -97,6 +97,29 @@
 					</enum>
 					<enum name="checkhangup">
 						<para>R/O Whether the channel is hanging up (1/0)</para>
+					</enum>
+					<enum name="hangup_handler_pop">
+						<para>W/O Replace the most recently added hangup handler
+						with a new hangup handler on the channel if supplied.  The
+						assigned string is passed to the Gosub application when
+						the channel is hung up.  Any optionally omitted context
+						and exten are supplied by the channel pushing the handler
+						before it is pushed.</para>
+					</enum>
+					<enum name="hangup_handler_push">
+						<para>W/O Push a hangup handler onto the channel hangup
+						handler stack.  The assigned string is passed to the
+						Gosub application when the channel is hung up.  Any
+						optionally omitted context and exten are supplied by the
+						channel pushing the handler before it is pushed.</para>
+					</enum>
+					<enum name="hangup_handler_wipe">
+						<para>W/O Wipe the entire hangup handler stack and replace
+						with a new hangup handler on the channel if supplied.  The
+						assigned string is passed to the Gosub application when
+						the channel is hung up.  Any optionally omitted context
+						and exten are supplied by the channel pushing the handler
+						before it is pushed.</para>
 					</enum>
 					<enum name="language">
 						<para>R/W language for sounds played.</para>
@@ -523,6 +546,17 @@
 				break;
 			}
 		}
+	} else if (!strcasecmp(data, "hangup_handler_pop")) {
+		/* Pop one hangup handler before pushing the new handler. */
+		ast_pbx_hangup_handler_pop(chan);
+		ast_pbx_hangup_handler_push(chan, value);
+	} else if (!strcasecmp(data, "hangup_handler_push")) {
+		ast_pbx_hangup_handler_push(chan, value);
+	} else if (!strcasecmp(data, "hangup_handler_wipe")) {
+		/* Pop all hangup handlers before pushing the new handler. */
+		while (ast_pbx_hangup_handler_pop(chan)) {
+		}
+		ast_pbx_hangup_handler_push(chan, value);
 	} else if (!strncasecmp(data, "secure_bridge_", 14)) {
 		struct ast_datastore *ds;
 		struct ast_secure_call_store *store;

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Fri Jun 29 12:02:32 2012
@@ -731,6 +731,15 @@
 	T38_STATE_NEGOTIATED,	/*!< T38 established */
 };
 
+/*! Hangup handler instance node. */
+struct ast_hangup_handler {
+	/*! Next hangup handler node. */
+	AST_LIST_ENTRY(ast_hangup_handler) node;
+	/*! Hangup handler arg string passed to the Gosub application */
+	char args[0];
+};
+
+AST_LIST_HEAD_NOLOCK(ast_hangup_handler_list, ast_hangup_handler);
 AST_LIST_HEAD_NOLOCK(ast_datastore_list, ast_datastore);
 AST_LIST_HEAD_NOLOCK(ast_autochan_list, ast_autochan);
 AST_LIST_HEAD_NOLOCK(ast_readq_list, ast_frame);
@@ -2196,6 +2205,17 @@
  * \retval -1 error, or the channel has been hungup
  */
 int ast_autoservice_stop(struct ast_channel *chan);
+
+/*!
+ * \brief Put chan into autoservice while hanging up peer.
+ * \since 11.0
+ *
+ * \param chan Chan to put into autoservice.
+ * \param peer Chan to run hangup handlers and hangup.
+ *
+ * \return Nothing
+ */
+void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer);
 
 /*!
  * \brief Ignore certain frame types
@@ -3748,6 +3768,7 @@
 void ast_channel_varshead_set(struct ast_channel *chan, struct varshead *value);
 
 /* List getters */
+struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan);
 struct ast_datastore_list *ast_channel_datastores(struct ast_channel *chan);
 struct ast_autochan_list *ast_channel_autochans(struct ast_channel *chan);
 struct ast_readq_list *ast_channel_readq(struct ast_channel *chan);

Modified: trunk/include/asterisk/pbx.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/pbx.h?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/include/asterisk/pbx.h (original)
+++ trunk/include/asterisk/pbx.h Fri Jun 29 12:02:32 2012
@@ -368,6 +368,71 @@
 enum ast_pbx_result ast_pbx_run_args(struct ast_channel *c, struct ast_pbx_args *args);
 
 /*!
+ * \brief Run the h exten from the given context.
+ * \since 11.0
+ *
+ * \param chan Channel to run the h exten on.
+ * \param context Context the h exten is in.
+ *
+ * \return Nothing
+ */
+void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context);
+
+/*!
+ * \brief Run all hangup handlers on the channel.
+ * \since 11.0
+ *
+ * \param chan Channel to run the hangup handlers on.
+ *
+ * \note Absolutely _NO_ channel locks should be held before calling this function.
+ *
+ * \retval Zero if no hangup handlers run.
+ * \retval non-zero if hangup handlers were run.
+ */
+int ast_pbx_hangup_handler_run(struct ast_channel *chan);
+
+/*!
+ * \brief Init the hangup handler container on a channel.
+ * \since 11.0
+ *
+ * \param chan Channel to init the hangup handler container on.
+ *
+ * \return Nothing
+ */
+void ast_pbx_hangup_handler_init(struct ast_channel *chan);
+
+/*!
+ * \brief Destroy the hangup handler container on a channel.
+ * \since 11.0
+ *
+ * \param chan Channel to destroy the hangup handler container on.
+ *
+ * \return Nothing
+ */
+void ast_pbx_hangup_handler_destroy(struct ast_channel *chan);
+
+/*!
+ * \brief Pop the top of the channel hangup handler stack.
+ * \since 11.0
+ *
+ * \param chan Channel to push the hangup handler onto.
+ *
+ * \retval TRUE if a handler was popped off of the stack.
+ */
+int ast_pbx_hangup_handler_pop(struct ast_channel *chan);
+
+/*!
+ * \brief Push the given hangup handler onto the channel hangup handler stack.
+ * \since 11.0
+ *
+ * \param chan Channel to push the hangup handler onto.
+ * \param handler Gosub application parameter string.
+ *
+ * \return Nothing
+ */
+void ast_pbx_hangup_handler_push(struct ast_channel *chan, const char *handler);
+
+/*!
  * \brief Add and extension to an extension context.
  *
  * \param context context to add the extension to

Modified: trunk/main/autoservice.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/autoservice.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/main/autoservice.c (original)
+++ trunk/main/autoservice.c Fri Jun 29 12:02:32 2012
@@ -313,6 +313,16 @@
 	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;

Modified: trunk/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/channel.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/main/channel.c (original)
+++ trunk/main/channel.c Fri Jun 29 12:02:32 2012
@@ -1107,6 +1107,7 @@
 	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 @@
 		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 @@
 		ast_cel_check_retire_linkedid(chan);
 	}
 
+	ast_pbx_hangup_handler_destroy(chan);
+
+	ast_channel_lock(chan);
+
 	/* Get rid of each of the data stores on the channel */
-	ast_channel_lock(chan);
 	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_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 @@
 
 	/* 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 @@
 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 @@
 	}
 
 	/* 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 @@
 			(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 @@
 	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 @@
 
 	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 @@
 	 * 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,32 +6950,17 @@
 		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);

Modified: trunk/main/channel_internal_api.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/channel_internal_api.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/main/channel_internal_api.c (original)
+++ trunk/main/channel_internal_api.c Fri Jun 29 12:02:32 2012
@@ -137,6 +137,7 @@
 	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 @@
 {
 	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;

Modified: trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/main/features.c (original)
+++ trunk/main/features.c Fri Jun 29 12:02:32 2012
@@ -1077,10 +1077,10 @@
 			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 @@
 	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 @@
 		}
 
 		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 @@
 
 		/* 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 @@
 
 	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 @@
 
 	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 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 @@
 	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 @@
 		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);
+
+		ast_channel_lock(chan);
 
 		/* swap it back */
-		ast_channel_lock(chan);
 		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 @@
 				break;
 			}
 			if (res) {
-				ast_hangup(peer);
+				ast_autoservice_chan_hangup_peer(chan, peer);
 				parkinglot_unref(parkinglot);
 				return -1;
 			}
@@ -5549,7 +5526,7 @@
 		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 @@
 		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 @@
 			"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 @@
 				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 @@
 
 			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);

Modified: trunk/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/pbx.c?view=diff&rev=369493&r1=369492&r2=369493
==============================================================================
--- trunk/main/pbx.c (original)
+++ trunk/main/pbx.c Fri Jun 29 12:02:32 2012
@@ -5342,6 +5342,360 @@
 	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>

[... 307 lines stripped ...]



More information about the asterisk-commits mailing list