[asterisk-commits] rmudgett: branch rmudgett/hangup_handlers r368992 - in /team/rmudgett/hangup_...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Jun 14 20:01:23 CDT 2012


Author: rmudgett
Date: Thu Jun 14 20:01:19 2012
New Revision: 368992

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=368992
Log:
Commit current hangup handler work.

* Base hangup handler execution code.
* CHANNEL(hangup_handler)
* CLI "core show hanguphandlers <channel>" and "core show hanguphandlers all"
* Dial supports hangup handlers.

Needs more testing and bridge peer calling hangup handlers where needed.

Modified:
    team/rmudgett/hangup_handlers/.cleancount
    team/rmudgett/hangup_handlers/apps/app_dial.c
    team/rmudgett/hangup_handlers/channels/chan_local.c
    team/rmudgett/hangup_handlers/funcs/func_channel.c
    team/rmudgett/hangup_handlers/include/asterisk/channel.h
    team/rmudgett/hangup_handlers/include/asterisk/pbx.h
    team/rmudgett/hangup_handlers/main/channel.c
    team/rmudgett/hangup_handlers/main/channel_internal_api.c
    team/rmudgett/hangup_handlers/main/features.c
    team/rmudgett/hangup_handlers/main/pbx.c

Modified: team/rmudgett/hangup_handlers/.cleancount
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/.cleancount?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/.cleancount (original)
+++ team/rmudgett/hangup_handlers/.cleancount Thu Jun 14 20:01:19 2012
@@ -1,3 +1,1 @@
-39
-
-
+40

Modified: team/rmudgett/hangup_handlers/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/apps/app_dial.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/apps/app_dial.c (original)
+++ team/rmudgett/hangup_handlers/apps/app_dial.c Thu Jun 14 20:01:19 2012
@@ -1728,6 +1728,28 @@
 	return 0;
 }
 
+/*!
+ * \internal
+ * \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
+ */
+static void autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
+{
+	if (chan) {
+		ast_autoservice_start(chan);
+	}
+	ast_pbx_hangup_handler_run(peer);
+	ast_hangup(peer);
+	if (chan) {
+		ast_autoservice_stop(chan);
+	}
+}
+
 static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
 	struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
 {
@@ -1857,7 +1879,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! */
+		autoservice_chan_hangup_peer(chan, peer);
 		return -1;
 	}
 }
@@ -2750,7 +2773,7 @@
 				if (active_chan) {
 					struct ast_frame *fr = ast_read(active_chan);
 					if (!fr) {
-						ast_hangup(peer);
+						autoservice_chan_hangup_peer(chan, peer);
 						res = -1;
 						goto done;
 					}
@@ -2766,7 +2789,7 @@
 							switch (fr->subclass.integer) {
 								case AST_CONTROL_HANGUP:
 									ast_frfree(fr);
-									ast_hangup(peer);
+									autoservice_chan_hangup_peer(chan, peer);
 									res = -1;
 									goto done;
 								default:
@@ -2796,7 +2819,9 @@
 			ast_channel_context_set(peer, ast_channel_context(chan));
 			ast_channel_exten_set(peer, ast_channel_exten(chan));
 			ast_channel_priority_set(peer, ast_channel_priority(chan) + 2);
-			ast_pbx_start(peer);
+			if (ast_pbx_start(peer)) {
+				autoservice_chan_hangup_peer(chan, peer);
+			}
 			hanguptree(&out_chans, NULL, ast_test_flag64(&opts, OPT_CANCEL_ELSEWHERE) ? 1 : 0);
 			if (continue_exec)
 				*continue_exec = 1;
@@ -3001,7 +3026,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);
+				autoservice_chan_hangup_peer(chan, peer);
 				res = -1;
 				goto done;
 			}
@@ -3021,47 +3046,30 @@
 		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 */
-		}
-		if (!ast_check_hangup(peer) && ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) {
-			if(!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
-				ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
-				ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
-			} else { /* F() */
+			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)) {
 				int goto_res;
-				goto_res = ast_goto_if_exists(peer, ast_channel_context(chan), ast_channel_exten(chan), (ast_channel_priority(chan)) + 1); 
-				if (goto_res == AST_PBX_GOTO_FAILED) {
-					ast_hangup(peer);
+
+				if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
+					ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
+					goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
+				} else { /* F() */
+					goto_res = ast_goto_if_exists(peer, ast_channel_context(chan),
+						ast_channel_exten(chan), ast_channel_priority(chan) + 1); 
+				}
+				if (!goto_res && !ast_pbx_start(peer)) {
+					/* The peer is now running its own PBX. */
 					goto out;
 				}
-			}
-			ast_pbx_start(peer);
-		} else {
-			if (!ast_check_hangup(chan))
+			} else {
 				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
-			ast_hangup(peer);
-		}
+			}
+		}
+		autoservice_chan_hangup_peer(chan, peer);
 	}
 out:
 	if (moh) {

Modified: team/rmudgett/hangup_handlers/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/channels/chan_local.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/channels/chan_local.c (original)
+++ team/rmudgett/hangup_handlers/channels/chan_local.c Thu Jun 14 20:01:19 2012
@@ -238,6 +238,12 @@
 		return -1;
 	}
 
+	if (!strcmp(write_info->function, "CHANNEL")
+		&& !strcasecmp(write_info->data, "hangup_handler")) {
+		/* Block CHANNEL(hangup_handler) writes to the other local channel. */
+		return 0;
+	}
+
 	/* get the tech pvt */
 	if (!(p = ast_channel_tech_pvt(ast))) {
 		return -1;

Modified: team/rmudgett/hangup_handlers/funcs/func_channel.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/funcs/func_channel.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/funcs/func_channel.c (original)
+++ team/rmudgett/hangup_handlers/funcs/func_channel.c Thu Jun 14 20:01:19 2012
@@ -97,6 +97,13 @@
 					</enum>
 					<enum name="checkhangup">
 						<para>R/O Whether the channel is hanging up (1/0)</para>
+					</enum>
+					<enum name="hangup_handler">
+						<para>W/O Add a hangup handler to the channel.  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 adding the handler
+						before it is saved.</para>
 					</enum>
 					<enum name="language">
 						<para>R/W language for sounds played.</para>
@@ -523,6 +530,8 @@
 				break;
 			}
 		}
+	} else if (!strcasecmp(data, "hangup_handler")) {
+		ast_pbx_hangup_handler_add(chan, value);
 	} else if (!strncasecmp(data, "secure_bridge_", 14)) {
 		struct ast_datastore *ds;
 		struct ast_secure_call_store *store;

Modified: team/rmudgett/hangup_handlers/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/include/asterisk/channel.h?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/include/asterisk/channel.h (original)
+++ team/rmudgett/hangup_handlers/include/asterisk/channel.h Thu Jun 14 20:01:19 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);
@@ -3739,6 +3748,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: team/rmudgett/hangup_handlers/include/asterisk/pbx.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/include/asterisk/pbx.h?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/include/asterisk/pbx.h (original)
+++ team/rmudgett/hangup_handlers/include/asterisk/pbx.h Thu Jun 14 20:01:19 2012
@@ -368,6 +368,41 @@
 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 Add the given hangup handler to the channel.
+ * \since 11.0
+ *
+ * \param chan Channel to add the hangup handler to.
+ * \param handler Gosub application parameter string.
+ *
+ * \return Nothing
+ */
+void ast_pbx_hangup_handler_add(struct ast_channel *chan, const char *handler);
+
+/*!
  * \brief Add and extension to an extension context.
  *
  * \param context context to add the extension to

Modified: team/rmudgett/hangup_handlers/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/main/channel.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/main/channel.c (original)
+++ team/rmudgett/hangup_handlers/main/channel.c Thu Jun 14 20:01:19 2012
@@ -1103,6 +1103,7 @@
 	headp = ast_channel_varshead(tmp);
 	AST_LIST_HEAD_INIT_NOLOCK(headp);
 
+	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_hangup_handlers(tmp));
 	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp));
 
 	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_autochans(tmp));
@@ -1179,6 +1180,9 @@
 		return NULL;
 	}
 
+	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_hangup_handlers(tmp));
+	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp));
+
 	headp = ast_channel_varshead(tmp);
 	AST_LIST_HEAD_INIT_NOLOCK(headp);
 
@@ -2185,6 +2189,7 @@
 	struct ast_var_t *vardata;
 	struct ast_frame *f;
 	struct varshead *headp;
+	struct ast_hangup_handler *h_handler;
 	struct ast_datastore *datastore;
 	char device_name[AST_CHANNEL_NAME];
 	struct ast_callid *callid;
@@ -2194,8 +2199,14 @@
 		ast_cel_check_retire_linkedid(chan);
 	}
 
+	ast_channel_lock(chan);
+
+	/* Get rid of each of the hangup handlers on the channel */
+	while ((h_handler = AST_LIST_REMOVE_HEAD(ast_channel_hangup_handlers(chan), node))) {
+		ast_free(h_handler);
+	}
+
 	/* 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);
@@ -2312,10 +2323,21 @@
 static void ast_dummy_channel_destructor(void *obj)
 {
 	struct ast_channel *chan = obj;
+	struct ast_hangup_handler *h_handler;
+	struct ast_datastore *datastore;
 	struct ast_var_t *vardata;
 	struct varshead *headp;
 
-	headp = ast_channel_varshead(chan);
+	/* Get rid of each of the hangup handlers on the channel (Just in case) */
+	while ((h_handler = AST_LIST_REMOVE_HEAD(ast_channel_hangup_handlers(chan), node))) {
+		ast_free(h_handler);
+	}
+
+	/* 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));
@@ -2324,6 +2346,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);
 
@@ -6534,6 +6557,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;
@@ -6756,6 +6780,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))) {

Modified: team/rmudgett/hangup_handlers/main/channel_internal_api.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/main/channel_internal_api.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/main/channel_internal_api.c (original)
+++ team/rmudgett/hangup_handlers/main/channel_internal_api.c Thu Jun 14 20:01:19 2012
@@ -136,6 +136,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 */
@@ -883,6 +884,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: team/rmudgett/hangup_handlers/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/main/features.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/main/features.c (original)
+++ team/rmudgett/hangup_handlers/main/features.c Thu Jun 14 20:01:19 2012
@@ -4166,7 +4166,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;
@@ -4176,7 +4175,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));
@@ -4608,105 +4608,80 @@
 		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));
-		}
+		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_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;

Modified: team/rmudgett/hangup_handlers/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/hangup_handlers/main/pbx.c?view=diff&rev=368992&r1=368991&r2=368992
==============================================================================
--- team/rmudgett/hangup_handlers/main/pbx.c (original)
+++ team/rmudgett/hangup_handlers/main/pbx.c Thu Jun 14 20:01:19 2012
@@ -5337,6 +5337,267 @@
 	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) {
+			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);
+}
+
+/* BUGBUG need to examine all ast_bridge_call() to see about calling ast_pbx_hangup_handler_run(). */
+int ast_pbx_hangup_handler_run(struct ast_channel *chan)
+{
+	struct ast_hangup_handler_list *list;
+	struct ast_hangup_handler *h_handler;
+
+	ast_channel_lock(chan);
+	list = ast_channel_hangup_handlers(chan);
+	if (AST_LIST_EMPTY(list)) {
+		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 (;;) {
+		list = ast_channel_hangup_handlers(chan);
+		h_handler = AST_LIST_REMOVE_HEAD(list, node);
+		if (!h_handler) {
+			ast_channel_unlock(chan);
+			break;
+		}
+
+		manager_event(EVENT_FLAG_CALL, "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);
+	}
+	return 1;
+}
+
+void ast_pbx_hangup_handler_add(struct ast_channel *chan, const char *handler)
+{
+	struct ast_hangup_handler_list *list;
+	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);
+
+	list = ast_channel_hangup_handlers(chan);
+	AST_LIST_INSERT_HEAD(list, h_handler, node);
+
+	manager_event(EVENT_FLAG_CALL, "HangupHandlerAdd",
+		"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 *list;
+	struct ast_hangup_handler *h_handler;
+	int first = 1;
+
+	ast_channel_lock(chan);
+	list = ast_channel_hangup_handlers(chan);
+	AST_LIST_TRAVERSE(list, 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)
 {
@@ -5675,27 +5936,15 @@
 
 	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));
@@ -7585,6 +7834,8 @@
 #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"),




More information about the asterisk-commits mailing list