[svn-commits] mjordan: trunk r393777 - in /trunk: ./ include/asterisk/ main/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sun Jul 7 15:34:39 CDT 2013


Author: mjordan
Date: Sun Jul  7 15:34:38 2013
New Revision: 393777

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393777
Log:
Handle hangup logic in the Stasis message bus and consumers of Stasis messages

This patch does the following:
* It adds a new soft hangup flag AST_SOFTHANGUP_HANGUP_EXEC that is set when a
  channel is executing dialplan hangup logic, i.e., the 'h' extension or a
  hangup handler. Stasis messages now also convey the soft hangup flag so
  consumers of the messages can know when a channel is executing said
  hangup logic.
* It adds a new channel flag, AST_FLAG_DEAD, which is set when a channel is
  well and truly dead. Not just a zombie, but dead, Jim. Manager, CEL, CDRs,
  and other consumers of Stasis have been updated to look for this flag to
  know when the channel should by lying six feet under.
* The CDR engine has been updated to better handle a channel entering and
  leaving a bridge. Previously, a new CDR was automatically created when a
  channel left a bridge and put into the 'Pending' state; however, this
  way of handling CDRs made it difficult for the 'endbeforehexten' logic to
  work correctly - there was always a new CDR waiting in the hangup logic
  and, even if 'ended', wouldn't be the CDR people wanted to inspect in the
  hangup routine. This patch completely removes the Pending state and instead
  defers creation of the new CDR until it gets a new message that requires
  a new CDR.

Modified:
    trunk/CHANGES
    trunk/include/asterisk/cdr.h
    trunk/include/asterisk/channel.h
    trunk/include/asterisk/stasis_channels.h
    trunk/main/cdr.c
    trunk/main/cel.c
    trunk/main/channel.c
    trunk/main/channel_internal_api.c
    trunk/main/manager_channels.c
    trunk/main/pbx.c
    trunk/main/stasis_channels.c

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=393777&r1=393776&r2=393777
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Sun Jul  7 15:34:38 2013
@@ -286,8 +286,11 @@
  * CDRs will now be created between all participants in a bridge. For each
    pair of channels in a bridge, a CDR is created to represent the path of
    communication between those two endpoints. This lets an end user choose who
-   to bill for what during multi-party bridges or bridge operations during
-   transfer scenarios.
+   to bill for what during bridge operations with multiple parties.
+
+ * The duration, billsec, start, answer, and end times now reflect the times
+   associated with the current CDR for the channel, as opposed to a cumulative
+   measurement of all CDRs for that channel.
 
  * When a CDR is dispatched, user defined CDR variables from both parties are
    included in the resulting CDR. If both parties have the same variable, only

Modified: trunk/include/asterisk/cdr.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/cdr.h?view=diff&rev=393777&r1=393776&r2=393777
==============================================================================
--- trunk/include/asterisk/cdr.h (original)
+++ trunk/include/asterisk/cdr.h Sun Jul  7 15:34:38 2013
@@ -178,7 +178,7 @@
  *
  * The following transitions can occur while in the Bridge state:
  * \li If a \ref ast_bridge_blob_type message indicating a leave is received,
- * the state transitions to the Pending state
+ * the state transitions to the Finalized state.
  *
  * \par Parked
  *
@@ -203,27 +203,7 @@
  *
  * The following transitions can occur while in the Parked state:
  * \li If a \ref ast_bridge_blob_type message indicating a leave is received,
- * the state transitions to the Pending state
- *
- * \par Pending
- *
- * After a channel leaves a bridge, we often don't know what's going to happen
- * to it. It can enter another bridge; it can be hung up; it can continue on
- * in the dialplan. It can even enter into limbo! Pending holds the state of the
- * CDR until we get a subsequent Stasis message telling us what should happen.
- *
- * The following transitions can occur while in the Pending state:
- * \li If a \ref ast_bridge_blob_type message is received, a new CDR is created
- * and it is transitioned to the Bridge state
- * \li If a \ref ast_channel_dial_type indicating a Dial Begin is received, a
- * new CDR is created and it is transitioned to the Dial state
- * \li If a \ref ast_channel_cache_update is received indicating a change in
- * Context/Extension/Priority, a new CDR is created and transitioned to the
- * Single state. If the update indicates that the party has been hung up, the
- * CDR is transitioned to the Finalized state.
- * \li If a \ref ast_bridge_blob_type message indicating an entrance to a
- * holding bridge with a subclass type of "parking" is received, the CDR is
- * transitioned to the Parked state.
+ * the state transitions to the Finalized state
  *
  * \par Finalized
  *

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=393777&r1=393776&r2=393777
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Sun Jul  7 15:34:38 2013
@@ -927,6 +927,12 @@
 	 * This flag indicates that the channel was originated.
 	 */
 	AST_FLAG_ORIGINATED = (1 << 23),
+	/*!
+	 * The channel is well and truly dead. Once this is set and published, no further
+	 * actions should be taken upon the channel, and no further publications should
+	 * occur.
+	 */
+	AST_FLAG_DEAD = (1 << 24),
 };
 
 /*! \brief ast_bridge_config flags */
@@ -1018,8 +1024,12 @@
 	 * instead of actually hanging up.
 	 */
 	AST_SOFTHANGUP_UNBRIDGE =  (1 << 6),
-
-
+	/*!
+	 * Used to indicate that the channel is currently executing hangup
+	 * logic in the dialplan. The channel has been hungup when this is
+	 * set.
+	 */
+	AST_SOFTHANGUP_HANGUP_EXEC = (1 << 7),
 	/*!
 	 * \brief All softhangup flags.
 	 *

Modified: trunk/include/asterisk/stasis_channels.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_channels.h?view=diff&rev=393777&r1=393776&r2=393777
==============================================================================
--- trunk/include/asterisk/stasis_channels.h (original)
+++ trunk/include/asterisk/stasis_channels.h Sun Jul  7 15:34:38 2013
@@ -68,6 +68,7 @@
 	int hangupcause;                        /*!< Why is the channel hanged up. See causes.h */
 	int caller_pres;                        /*!< Caller ID presentation. */
 	struct ast_flags flags;                 /*!< channel flags of AST_FLAG_ type */
+	struct ast_flags softhangup_flags;      /*!< softhangup channel flags */
 	struct varshead *manager_vars;          /*!< Variables to be appended to manager events */
 };
 

Modified: trunk/main/cdr.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/cdr.c?view=diff&rev=393777&r1=393776&r2=393777
==============================================================================
--- trunk/main/cdr.c (original)
+++ trunk/main/cdr.c Sun Jul  7 15:34:38 2013
@@ -120,12 +120,17 @@
 					</description>
 				</configOption>
 				<configOption name="endbeforehexten">
-					<synopsis>End the CDR before executing the "h" extension</synopsis>
-					<description><para>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 <literal>h</literal> extension and hangup handlers so that CDR values such as <literal>end</literal> and
-					<literal>"billsec"</literal> may be retrieved inside of this extension.
-					The default value is "no".</para>
+					<synopsis>Don't produce CDRs while executing hangup logic</synopsis>
+					<description>
+						<para>As each CDR for a channel is finished, its end time is updated
+						and the CDR is finalized. When a channel is hung up and hangup
+						logic is present (in the form of a hangup handler or the
+						<literal>h</literal> extension), a new CDR is generated for the
+						channel. Any statistics are gathered from this new CDR. By enabling
+						this option, no new CDR is created for the dialplan logic that is
+						executed in <literal>h</literal> extensions or attached hangup handler
+						subroutines. The default value is <literal>no</literal>, indicating
+						that a CDR will be generated during hangup logic.</para>
 					</description>
 				</configOption>
 				<configOption name="initiatedseconds">
@@ -335,6 +340,26 @@
 
 struct cdr_object;
 
+/*! \brief Return types for \ref process_bridge_enter functions */
+enum process_bridge_enter_results {
+	/*!
+	 * The CDR was the only party in the bridge.
+	 */
+	BRIDGE_ENTER_ONLY_PARTY,
+	/*!
+	 * The CDR was able to obtain a Party B from some other party already in the bridge
+	 */
+	BRIDGE_ENTER_OBTAINED_PARTY_B,
+	/*!
+	 * The CDR was not able to obtain a Party B
+	 */
+	BRIDGE_ENTER_NO_PARTY_B,
+	/*!
+	 * This CDR can't handle a bridge enter message and a new CDR needs to be created
+	 */
+	BRIDGE_ENTER_NEED_CDR,
+};
+
 /*!
  * \brief A virtual table used for \ref cdr_object.
  *
@@ -425,11 +450,11 @@
 	 * \param bridge The bridge that the Party A just entered into
 	 * \param channel The \ref ast_channel_snapshot for this CDR's Party A
 	 *
-	 * \retval 0 This CDR found a Party B for itself and updated it, or there
-	 * was no Party B to find (we're all alone)
-	 * \retval 1 This CDR couldn't find a Party B and channels were in the bridge
+	 * \retval process_bridge_enter_results Defines whether or not this CDR was able
+	 * to fully handle the bridge enter message.
 	 */
-	int (* const process_bridge_enter)(struct cdr_object *cdr,
+	enum process_bridge_enter_results (* const process_bridge_enter)(
+			struct cdr_object *cdr,
 			struct ast_bridge_snapshot *bridge,
 			struct ast_channel_snapshot *channel);
 
@@ -476,6 +501,7 @@
 };
 
 static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
+static enum process_bridge_enter_results base_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 static int base_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status);
 static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info);
@@ -483,7 +509,7 @@
 static void single_state_init_function(struct cdr_object *cdr);
 static void single_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
-static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static enum process_bridge_enter_results single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
@@ -513,7 +539,7 @@
 static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int dial_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
 static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status);
-static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static enum process_bridge_enter_results dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
  * \brief The virtual table for the Dial state.
@@ -540,7 +566,7 @@
 
 static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
 static int dialed_pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
-static int dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
+static enum process_bridge_enter_results dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 static int dialed_pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
@@ -582,7 +608,6 @@
  *
  * A \ref cdr_object from this state can go to:
  * * \ref finalized_state_fn_table
- * * \ref pending_state_fn_table
  */
 struct cdr_object_fn_table bridge_state_fn_table = {
 	.name = "Bridged",
@@ -592,36 +617,6 @@
 	.process_parked_channel = base_process_parked_channel,
 };
 
-static void pending_state_init_function(struct cdr_object *cdr);
-static int pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot);
-static int pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer);
-static int pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
-static int pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
-
-/*!
- * \brief The virtual table for the Pending state
- *
- * At certain times, we don't know where to go with the CDR. A good example is
- * when a channel leaves a bridge - we don't know if the channel is about to
- * be hung up; if it is about to go execute dialplan; dial someone; go into
- * another bridge, etc. At these times, the CDR goes into pending and observes
- * the messages that come in next to infer where the next logical place to go
- * is.
- *
- * In this state, a CDR can go anywhere!
- */
-struct cdr_object_fn_table bridged_pending_state_fn_table = {
-	.name = "Pending",
-	.init_function = pending_state_init_function,
-	.process_party_a = pending_state_process_party_a,
-	.process_dial_begin = pending_state_process_dial_begin,
-	.process_dial_end = base_process_dial_end,
-	.process_bridge_enter = pending_state_process_bridge_enter,
-	.process_parking_bridge_enter = pending_state_process_parking_bridge_enter,
-	.process_bridge_leave = base_process_bridge_leave,
-	.process_parked_channel = base_process_parked_channel,
-};
-
 static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel);
 
 /*!
@@ -653,6 +648,7 @@
 	.name = "Finalized",
 	.init_function = finalized_state_init_function,
 	.process_party_a = finalized_state_process_party_a,
+	.process_bridge_enter = base_process_bridge_enter,
 };
 
 /*! \brief A wrapper object around a snapshot.
@@ -899,6 +895,38 @@
 }
 
 /*!
+ * \brief Return whether or not a channel has changed its state in the dialplan, subject
+ * to endbeforehexten logic
+ *
+ * \param old_snapshot The previous state
+ * \param new_snapshot The new state
+ *
+ * \retval 0 if the state has not changed
+ * \retval 1 if the state changed
+ */
+static int snapshot_cep_changed(struct ast_channel_snapshot *old_snapshot,
+	struct ast_channel_snapshot *new_snapshot)
+{
+	RAII_VAR(struct module_config *, mod_cfg,
+		ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	/* If we ignore hangup logic, don't indicate that we're executing anything new */
+	if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)
+		&& ast_test_flag(&new_snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
+		return 0;
+	}
+
+	if (strcmp(new_snapshot->context, old_snapshot->context)
+		|| strcmp(new_snapshot->exten, old_snapshot->exten)
+		|| new_snapshot->priority != old_snapshot->priority
+		|| strcmp(new_snapshot->appl, old_snapshot->appl)) {
+		return 1;
+	}
+
+	return 0;
+}
+
+/*!
  * \brief Return whether or not a \ref ast_channel_snapshot is for a channel
  * that was created as the result of a dial operation
  *
@@ -955,11 +983,7 @@
  */
 static long cdr_object_get_duration(struct cdr_object *cdr)
 {
-	if (ast_tvzero(cdr->end)) {
-		return (long)(ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
-	} else {
-		return (long)(ast_tvdiff_ms(cdr->end, cdr->start) / 1000);
-	}
+	return (long)(ast_tvdiff_ms(ast_tvzero(cdr->end) ? ast_tvnow() : cdr->end, cdr->start) / 1000);
 }
 
 /*!
@@ -973,7 +997,7 @@
 	if (ast_tvzero(cdr->answer)) {
 		return 0;
 	}
-	ms = ast_tvdiff_ms(cdr->end, cdr->answer);
+	ms = ast_tvdiff_ms(ast_tvzero(cdr->end) ? ast_tvnow() : cdr->end, cdr->answer);
 	if (ast_test_flag(&mod_cfg->general->settings, CDR_INITIATED_SECONDS)
 		&& (ms % 1000 >= 500)) {
 		ms = (ms / 1000) + 1;
@@ -985,6 +1009,33 @@
 }
 
 /*!
+ * \internal
+ * \brief Set a variable on a CDR object
+ *
+ * \param headp The header pointer to the variable to set
+ * \param name The name of the variable
+ * \param value The value of the variable
+ */
+static void set_variable(struct varshead *headp, const char *name, const char *value)
+{
+	struct ast_var_t *newvariable;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
+		if (!strcasecmp(ast_var_name(newvariable), name)) {
+			AST_LIST_REMOVE_CURRENT(entries);
+			ast_var_delete(newvariable);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	if (value) {
+		newvariable = ast_var_assign(name, value);
+		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+	}
+}
+
+/*!
  * \brief Create a chain of \ref ast_cdr objects from a chain of \ref cdr_object
  * suitable for consumption by the registered CDR backends
  * \param cdr The \ref cdr_object to convert to a public record
@@ -994,16 +1045,16 @@
 static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
 {
 	struct ast_cdr *pub_cdr = NULL, *cdr_prev = NULL;
+	struct cdr_object *it_cdr;
 	struct ast_var_t *it_var, *it_copy_var;
 	struct ast_channel_snapshot *party_a;
 	struct ast_channel_snapshot *party_b;
 
-	while (cdr) {
+	for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
 		struct ast_cdr *cdr_copy;
 
 		/* Don't create records for CDRs where the party A was a dialed channel */
-		if (snapshot_is_dialed(cdr->party_a.snapshot)) {
-			cdr = cdr->next;
+		if (snapshot_is_dialed(it_cdr->party_a.snapshot)) {
 			continue;
 		}
 
@@ -1013,8 +1064,8 @@
 			return NULL;
 		}
 
-		party_a = cdr->party_a.snapshot;
-		party_b = cdr->party_b.snapshot;
+		party_a = it_cdr->party_a.snapshot;
+		party_b = it_cdr->party_b.snapshot;
 
 		/* Party A */
 		ast_assert(party_a != NULL);
@@ -1024,8 +1075,8 @@
 		ast_callerid_merge(cdr_copy->clid, sizeof(cdr_copy->clid), party_a->caller_name, party_a->caller_number, "");
 		ast_copy_string(cdr_copy->src, party_a->caller_number, sizeof(cdr_copy->src));
 		ast_copy_string(cdr_copy->uniqueid, party_a->uniqueid, sizeof(cdr_copy->uniqueid));
-		ast_copy_string(cdr_copy->lastapp, cdr->appl, sizeof(cdr_copy->lastapp));
-		ast_copy_string(cdr_copy->lastdata, cdr->data, sizeof(cdr_copy->lastdata));
+		ast_copy_string(cdr_copy->lastapp, it_cdr->appl, sizeof(cdr_copy->lastapp));
+		ast_copy_string(cdr_copy->lastdata, it_cdr->data, sizeof(cdr_copy->lastdata));
 		ast_copy_string(cdr_copy->dst, party_a->exten, sizeof(cdr_copy->dst));
 		ast_copy_string(cdr_copy->dcontext, party_a->context, sizeof(cdr_copy->dcontext));
 
@@ -1033,30 +1084,30 @@
 		if (party_b) {
 			ast_copy_string(cdr_copy->dstchannel, party_b->name, sizeof(cdr_copy->dstchannel));
 			ast_copy_string(cdr_copy->peeraccount, party_b->accountcode, sizeof(cdr_copy->peeraccount));
-			if (!ast_strlen_zero(cdr->party_b.userfield)) {
-				snprintf(cdr_copy->userfield, sizeof(cdr_copy->userfield), "%s;%s", cdr->party_a.userfield, cdr->party_b.userfield);
+			if (!ast_strlen_zero(it_cdr->party_b.userfield)) {
+				snprintf(cdr_copy->userfield, sizeof(cdr_copy->userfield), "%s;%s", it_cdr->party_a.userfield, it_cdr->party_b.userfield);
 			}
 		}
-		if (ast_strlen_zero(cdr_copy->userfield) && !ast_strlen_zero(cdr->party_a.userfield)) {
-			ast_copy_string(cdr_copy->userfield, cdr->party_a.userfield, sizeof(cdr_copy->userfield));
+		if (ast_strlen_zero(cdr_copy->userfield) && !ast_strlen_zero(it_cdr->party_a.userfield)) {
+			ast_copy_string(cdr_copy->userfield, it_cdr->party_a.userfield, sizeof(cdr_copy->userfield));
 		}
 
 		/* Timestamps/durations */
-		cdr_copy->start = cdr->start;
-		cdr_copy->answer = cdr->answer;
-		cdr_copy->end = cdr->end;
-		cdr_copy->billsec = cdr_object_get_billsec(cdr);
-		cdr_copy->duration = cdr_object_get_duration(cdr);
+		cdr_copy->start = it_cdr->start;
+		cdr_copy->answer = it_cdr->answer;
+		cdr_copy->end = it_cdr->end;
+		cdr_copy->billsec = cdr_object_get_billsec(it_cdr);
+		cdr_copy->duration = cdr_object_get_duration(it_cdr);
 
 		/* Flags and IDs */
-		ast_copy_flags(cdr_copy, &cdr->flags, AST_FLAGS_ALL);
-		ast_copy_string(cdr_copy->linkedid, cdr->linkedid, sizeof(cdr_copy->linkedid));
-		cdr_copy->disposition = cdr->disposition;
-		cdr_copy->sequence = cdr->sequence;
+		ast_copy_flags(cdr_copy, &it_cdr->flags, AST_FLAGS_ALL);
+		ast_copy_string(cdr_copy->linkedid, it_cdr->linkedid, sizeof(cdr_copy->linkedid));
+		cdr_copy->disposition = it_cdr->disposition;
+		cdr_copy->sequence = it_cdr->sequence;
 
 		/* Variables */
-		copy_variables(&cdr_copy->varshead, &cdr->party_a.variables);
-		AST_LIST_TRAVERSE(&cdr->party_b.variables, it_var, entries) {
+		copy_variables(&cdr_copy->varshead, &it_cdr->party_a.variables);
+		AST_LIST_TRAVERSE(&it_cdr->party_b.variables, it_var, entries) {
 			int found = 0;
 			AST_LIST_TRAVERSE(&cdr_copy->varshead, it_copy_var, entries) {
 				if (!strcmp(ast_var_name(it_var), ast_var_name(it_copy_var))) {
@@ -1077,7 +1128,6 @@
 			cdr_prev->next = cdr_copy;
 			cdr_prev = cdr_copy;
 		}
-		cdr = cdr->next;
 	}
 
 	return pub_cdr;
@@ -1188,7 +1238,14 @@
  */
 static void cdr_object_check_party_a_hangup(struct cdr_object *cdr)
 {
-	if (ast_test_flag(&cdr->party_a.snapshot->flags, AST_FLAG_ZOMBIE)
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)
+		&& ast_test_flag(&cdr->party_a.snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
+		cdr_object_finalize(cdr);
+	}
+
+	if (ast_test_flag(&cdr->party_a.snapshot->flags, AST_FLAG_DEAD)
 		&& cdr->fn_table != &finalized_state_fn_table) {
 		cdr_object_transition_state(cdr, &finalized_state_fn_table);
 	}
@@ -1207,35 +1264,6 @@
 		CDR_DEBUG(mod_cfg, "%p - Set answered time to %ld.%06ld\n", cdr,
 			cdr->answer.tv_sec,
 			(long)cdr->answer.tv_usec);
-	}
-}
-
-/*!
- * \internal
- * \brief Set a variable on a CDR object
- *
- * \param headp The header pointer to the variable to set
- * \param name The name of the variable
- * \param value The value of the variable
- *
- * CDRs that are in a hungup state cannot have their variables set.
- */
-static void set_variable(struct varshead *headp, const char *name, const char *value)
-{
-	struct ast_var_t *newvariable;
-
-	AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
-		if (!strcasecmp(ast_var_name(newvariable), name)) {
-			AST_LIST_REMOVE_CURRENT(entries);
-			ast_var_delete(newvariable);
-			break;
-		}
-	}
-	AST_LIST_TRAVERSE_SAFE_END;
-
-	if (value) {
-		newvariable = ast_var_assign(name, value);
-		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
 	}
 }
 
@@ -1316,6 +1344,12 @@
 	/* In general, most things shouldn't get a dial end. */
 	ast_assert(0);
 	return 0;
+}
+
+static enum process_bridge_enter_results base_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+{
+	/* Base process bridge enter simply indicates that we can't handle it */
+	return BRIDGE_ENTER_NEED_CDR;
 }
 
 static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info)
@@ -1424,12 +1458,12 @@
 	return 1;
 }
 
-static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+static enum process_bridge_enter_results single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
 	struct ao2_iterator *it_cdrs;
 	struct cdr_object *cand_cdr_master;
 	char *bridge_id = ast_strdupa(bridge->uniqueid);
-	int success = 1;
+	int success = 0;
 
 	ast_string_field_set(cdr, bridge, bridge->uniqueid);
 
@@ -1439,7 +1473,7 @@
 	if (!it_cdrs) {
 		/* No one in the bridge yet! */
 		cdr_object_transition_state(cdr, &bridge_state_fn_table);
-		return 0;
+		return BRIDGE_ENTER_ONLY_PARTY;
 	}
 
 	while ((cand_cdr_master = ao2_iterator_next(it_cdrs))) {
@@ -1458,7 +1492,7 @@
 				continue;
 			}
 			/* We successfully got a party B - break out */
-			success = 0;
+			success = 1;
 			break;
 		}
 		ao2_unlock(cand_cdr_master);
@@ -1470,7 +1504,11 @@
 	cdr_object_transition_state(cdr, &bridge_state_fn_table);
 
 	/* Success implies that we have a Party B */
-	return success;
+	if (success) {
+		return BRIDGE_ENTER_OBTAINED_PARTY_B;
+	}
+
+	return BRIDGE_ENTER_NO_PARTY_B;
 }
 
 static int single_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
@@ -1492,7 +1530,7 @@
 	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
 	/* If party B hangs up, finalize this CDR */
-	if (ast_test_flag(&cdr->party_b.snapshot->flags, AST_FLAG_ZOMBIE)) {
+	if (ast_test_flag(&cdr->party_b.snapshot->flags, AST_FLAG_DEAD)) {
 		cdr_object_transition_state(cdr, &finalized_state_fn_table);
 	}
 }
@@ -1563,12 +1601,12 @@
 	return 0;
 }
 
-static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+static enum process_bridge_enter_results dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
 	struct ao2_iterator *it_cdrs;
 	char *bridge_id = ast_strdupa(bridge->uniqueid);
 	struct cdr_object *cand_cdr_master;
-	int success = 1;
+	int success = 0;
 
 	ast_string_field_set(cdr, bridge, bridge->uniqueid);
 
@@ -1578,7 +1616,7 @@
 	if (!it_cdrs) {
 		/* No one in the bridge yet! */
 		cdr_object_transition_state(cdr, &bridge_state_fn_table);
-		return 0;
+		return BRIDGE_ENTER_ONLY_PARTY;
 	}
 
 	while ((cand_cdr_master = ao2_iterator_next(it_cdrs))) {
@@ -1610,7 +1648,7 @@
 			if (!cand_cdr->party_b.snapshot) {
 				cdr_object_finalize(cand_cdr);
 			}
-			success = 0;
+			success = 1;
 			break;
 		}
 		ao2_unlock(cand_cdr_master);
@@ -1622,7 +1660,10 @@
 	cdr_object_transition_state(cdr, &bridge_state_fn_table);
 
 	/* Success implies that we have a Party B */
-	return success;
+	if (success) {
+		return BRIDGE_ENTER_OBTAINED_PARTY_B;
+	}
+	return BRIDGE_ENTER_NO_PARTY_B;
 }
 
 /* DIALED PENDING STATE */
@@ -1632,10 +1673,7 @@
 	/* If we get a CEP change, we're executing dialplan. If we have a Party B
 	 * that means we need a new CDR; otherwise, switch us over to single.
 	 */
-	if (strcmp(snapshot->context, cdr->party_a.snapshot->context)
-		|| strcmp(snapshot->exten, cdr->party_a.snapshot->exten)
-		|| snapshot->priority != cdr->party_a.snapshot->priority
-		|| strcmp(snapshot->appl, cdr->party_a.snapshot->appl)) {
+	if (snapshot_cep_changed(cdr->party_a.snapshot, snapshot)) {
 		if (cdr->party_b.snapshot) {
 			cdr_object_transition_state(cdr, &finalized_state_fn_table);
 			cdr->fn_table->process_party_a(cdr, snapshot);
@@ -1650,7 +1688,7 @@
 	return 0;
 }
 
-static int dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
+static enum process_bridge_enter_results dialed_pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
 	cdr_object_transition_state(cdr, &dial_state_fn_table);
 	return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
@@ -1680,7 +1718,7 @@
 	cdr_object_swap_snapshot(&cdr->party_b, snapshot);
 
 	/* If party B hangs up, finalize this CDR */
-	if (ast_test_flag(&cdr->party_b.snapshot->flags, AST_FLAG_ZOMBIE)) {
+	if (ast_test_flag(&cdr->party_b.snapshot->flags, AST_FLAG_DEAD)) {
 		cdr_object_transition_state(cdr, &finalized_state_fn_table);
 	}
 }
@@ -1700,53 +1738,6 @@
 	return 0;
 }
 
-/* PENDING STATE */
-
-static void pending_state_init_function(struct cdr_object *cdr)
-{
-	ast_cdr_set_property(cdr->name, AST_CDR_FLAG_DISABLE);
-}
-
-static int pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
-{
-	if (ast_test_flag(&snapshot->flags, AST_FLAG_ZOMBIE)) {
-		return 0;
-	}
-
-	/* Ignore if we don't get a CEP change */
-	if (!strcmp(snapshot->context, cdr->party_a.snapshot->context)
-		&& !strcmp(snapshot->exten, cdr->party_a.snapshot->exten)
-		&& snapshot->priority == cdr->party_a.snapshot->priority) {
-		return 0;
-	}
-
-	cdr_object_transition_state(cdr, &single_state_fn_table);
-	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
-	cdr->fn_table->process_party_a(cdr, snapshot);
-	return 0;
-}
-
-static int pending_state_process_dial_begin(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
-{
-	cdr_object_transition_state(cdr, &single_state_fn_table);
-	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
-	return cdr->fn_table->process_dial_begin(cdr, caller, peer);
-}
-
-static int pending_state_process_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
-{
-	cdr_object_transition_state(cdr, &single_state_fn_table);
-	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
-	return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
-}
-
-static int pending_state_process_parking_bridge_enter(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
-{
-	cdr_object_transition_state(cdr, &single_state_fn_table);
-	ast_cdr_clear_property(cdr->name, AST_CDR_FLAG_DISABLE);
-	return cdr->fn_table->process_parking_bridge_enter(cdr, bridge, channel);
-}
-
 /* PARKED STATE */
 
 static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
@@ -1763,19 +1754,18 @@
 
 static void finalized_state_init_function(struct cdr_object *cdr)
 {
-	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
-
-	if (!ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)) {
-		return;
-	}
-
 	cdr_object_finalize(cdr);
 }
 
 static int finalized_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
 {
-	if (ast_test_flag(&cdr->party_a.snapshot->flags, AST_FLAG_ZOMBIE)) {
-		cdr_object_finalize(cdr);
+	RAII_VAR(struct module_config *, mod_cfg,
+		ao2_global_obj_ref(module_configs), ao2_cleanup);
+
+	/* If we ignore hangup logic, indicate that we don't need a new CDR */
+	if (ast_test_flag(&mod_cfg->general->settings, CDR_END_BEFORE_H_EXTEN)
+		&& ast_test_flag(&snapshot->softhangup_flags, AST_SOFTHANGUP_HANGUP_EXEC)) {
+		return 0;
 	}
 
 	/* Indicate that, if possible, we should get a new CDR */
@@ -1968,7 +1958,7 @@
 		return 0;
 	}
 
-	if (ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE)) {
+	if (ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD)) {
 		return 0;
 	}
 
@@ -1977,10 +1967,7 @@
 		return 0;
 	}
 
-	if (old_snapshot && !strcmp(old_snapshot->context, new_snapshot->context)
-			&& !strcmp(old_snapshot->exten, new_snapshot->exten)
-			&& old_snapshot->priority == new_snapshot->priority
-			&& !(strcmp(old_snapshot->appl, new_snapshot->appl))) {
+	if (old_snapshot && !snapshot_cep_changed(old_snapshot, new_snapshot)) {
 		return 0;
 	}
 
@@ -2102,13 +2089,10 @@
 		if (strcmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) {
 			continue;
 		}
-		if (!it_cdr->fn_table->process_bridge_leave(it_cdr, leave_data->bridge, leave_data->channel)) {
-			/* Update the end times for this CDR. We don't want to actually
-			 * finalize it, as the Party A will eventually need to leave, which
-			 * will switch the records to pending bridged.
-			 */
-			cdr_object_finalize(it_cdr);
-		}
+		/* It is our Party B, in our bridge. Set the end time and let the handler
+		 * transition our CDR appropriately when we leave the bridge.
+		 */
+		cdr_object_finalize(it_cdr);
 	}
 	return 0;
 }
@@ -2144,7 +2128,6 @@
 			ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
 			ao2_cleanup);
 	struct cdr_object *it_cdr;
-	struct cdr_object *pending_cdr;
 	struct bridge_leave_data leave_data = {
 		.bridge = bridge,
 		.channel = channel,
@@ -2181,14 +2164,6 @@
 	if (!left_bridge) {
 		ao2_unlock(cdr);
 		return;
-	}
-
-	/* Create a new pending record. If the channel decides to do something else,
-	 * the pending record will handle it - otherwise, if gets dropped.
-	 */
-	pending_cdr = cdr_object_create_and_append(cdr);
-	if (pending_cdr) {
-		cdr_object_transition_state(pending_cdr, &bridged_pending_state_fn_table);
 	}
 	ao2_unlock(cdr);
 
@@ -2518,8 +2493,9 @@
 {
 	RAII_VAR(struct module_config *, mod_cfg,
 			ao2_global_obj_ref(module_configs), ao2_cleanup);
-	int res = 1;
+	enum process_bridge_enter_results result;
 	struct cdr_object *it_cdr;
+	struct cdr_object *new_cdr;
 	struct cdr_object *handled_cdr = NULL;
 
 	ao2_lock(cdr);
@@ -2535,18 +2511,29 @@
 		if (it_cdr->fn_table->process_bridge_enter) {
 			CDR_DEBUG(mod_cfg, "%p - Processing bridge enter for %s\n", it_cdr,
 					channel->name);
-			res &= it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel);
-			if (!res && !handled_cdr) {
-				handled_cdr = it_cdr;
+			result = it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel);
+			switch (result) {
+			case BRIDGE_ENTER_ONLY_PARTY:
+				/* Fall through */
+			case BRIDGE_ENTER_OBTAINED_PARTY_B:
+				if (!handled_cdr) {
+					handled_cdr = it_cdr;
+				}
+			break;
+			case BRIDGE_ENTER_NEED_CDR:
+				/* Pass */
+			break;
+			case BRIDGE_ENTER_NO_PARTY_B:
+				/* We didn't win on any - end this CDR. If someone else comes in later
+				 * that is Party B to this CDR, it can re-activate this CDR.
+				 */
+				if (!handled_cdr) {
+					handled_cdr = it_cdr;
+				}
+				cdr_object_finalize(cdr);
+			break;
 			}
 		}
-	}
-
-	if (res) {
-		/* We didn't win on any - end this CDR. If someone else comes in later
-		 * that is Party B to this CDR, it can re-activate this CDR.
-		 */
-		cdr_object_finalize(cdr);
 	}
 
 	/* Create the new matchings, but only for either:
@@ -2556,10 +2543,18 @@
 	 *    a CDR joined a bridge and it wasn't Party A for anyone. We still need
 	 *    to make pairings with everyone in the bridge.
 	 */
-	if (!handled_cdr) {
-		handled_cdr = cdr->last;
-	}
-	handle_bridge_pairings(handled_cdr, bridge);
+	if (handled_cdr) {
+		handle_bridge_pairings(handled_cdr, bridge);
+	} else {
+		/* Nothing handled it - we need a new one! */
+		new_cdr = cdr_object_create_and_append(cdr);
+		if (new_cdr) {
+			/* This is guaranteed to succeed: the new CDR is created in the single state
+			 * and will be able to handle the bridge enter message
+			 */
+			handle_standard_bridge_enter_message(cdr, bridge, channel);
+		}
+	}
 	ao2_unlock(cdr);
 }
 
@@ -2869,10 +2864,11 @@
 }
 
 /* Read Only CDR variables */
-static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
-						  "lastapp", "lastdata", "start", "answer", "end", "duration",
-						  "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
-						  "userfield", "sequence", NULL };
+static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext",
+	"channel", "dstchannel", "lastapp", "lastdata", "start", "answer", "end", "duration",
+	"billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
+	"userfield", "sequence", "total_duration", "total_billsec", "first_start",
+	"first_answer", NULL };
 
 int ast_cdr_setvar(const char *channel_name, const char *name, const char *value)
 {
@@ -3020,11 +3016,11 @@
 	ao2_lock(cdr);
 
 	cdr_obj = cdr->last;
-
 	if (cdr_object_format_property(cdr_obj, name, value, length)) {
 		/* Property failed; attempt variable */
 		cdr_object_format_var_internal(cdr_obj, name, value, length);
 	}
+
 	ao2_unlock(cdr);
 
 	return 0;
@@ -3188,7 +3184,7 @@
 		if (!ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) &&
 				cdr->disposition < AST_CDR_ANSWERED &&
 				(ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
-			ast_log(AST_LOG_WARNING, "Skipping CDR since we weren't answered\n");
+			ast_debug(1, "Skipping CDR  for %s since we weren't answered\n", cdr->channel);
 			continue;
 		}
 
@@ -3311,7 +3307,6 @@
 			/* If the last CDR in the chain is finalized, don't allow a fork -
 			 * things are already dying at this point
 			 */
-			ast_log(AST_LOG_ERROR, "FARK\n");
 			return -1;
 		}
 
@@ -3655,13 +3650,15 @@
 				answer_time = it_cdr->answer;
 			}
 		}
-		/* Only CDRs when this was dialed are available; skip */
+
+		/* If there was no start time, then all CDRs were for a dialed channel; skip */
 		if (ast_tvzero(start_time)) {
 			ao2_ref(cdr, -1);
 			continue;
 		}
 		it_cdr = cdr->last;
-		end_time = ast_tvzero(cdr->last->end) ? ast_tvnow() : cdr->last->end;
+
+		end_time = ast_tvzero(it_cdr->end) ? ast_tvnow() : it_cdr->end;
 		cdr_get_tv(start_time, "%T", start_time_buffer, sizeof(start_time_buffer));
 		cdr_get_tv(answer_time, "%T", answer_time_buffer, sizeof(answer_time_buffer));
 		cdr_get_tv(end_time, "%T", end_time_buffer, sizeof(end_time_buffer));
@@ -3671,7 +3668,7 @@
 				start_time_buffer,
 				answer_time_buffer,
 				end_time_buffer,
-				(long)ast_tvdiff_ms(end_time, answer_time) / 1000,
+				ast_tvzero(answer_time) ? 0 : (long)ast_tvdiff_ms(end_time, answer_time) / 1000,
 				(long)ast_tvdiff_ms(end_time, start_time) / 1000);
 		ao2_ref(cdr, -1);
 	}
@@ -3829,18 +3826,32 @@
 
 static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
+	RAII_VAR(struct module_config *, mod_cfg, ao2_global_obj_ref(module_configs), ao2_cleanup);
+
 	switch (cmd) {
 	case CLI_INIT:
 		e->command = "cdr submit";
 		e->usage =
 			"Usage: cdr submit\n"
-			"       Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
+			"Posts all pending batched CDR data to the configured CDR\n"
+			"backend engine modules.\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc > 2)
+	if (a->argc > 2) {
 		return CLI_SHOWUSAGE;
+	}
+
+	if (!ast_test_flag(&mod_cfg->general->settings, CDR_ENABLED)) {
+		ast_cli(a->fd, "Cannot submit CDR batch: CDR engine disabled.\n");
+		return CLI_SUCCESS;
+	}
+
+	if (ast_test_flag(&mod_cfg->general->settings, CDR_BATCHMODE)) {
+		ast_cli(a->fd, "Cannot submit CDR batch: batch mode not enabled.\n");
+		return CLI_SUCCESS;
+	}
 
 	submit_unscheduled_batch();
 	ast_cli(a->fd, "Submitted CDRs to backend engines for processing.  This may take a while.\n");
@@ -3848,11 +3859,12 @@
 	return CLI_SUCCESS;
 }
 
-static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");

[... 184 lines stripped ...]



More information about the svn-commits mailing list