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.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393777 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Matthew Jordan 12 years ago
parent c54b26a18c
commit b193c2873d

@ -286,8 +286,11 @@ CDR (Call Detail Records)
* CDRs will now be created between all participants in a bridge. For each * 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 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 communication between those two endpoints. This lets an end user choose who
to bill for what during multi-party bridges or bridge operations during to bill for what during bridge operations with multiple parties.
transfer scenarios.
* 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 * 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 included in the resulting CDR. If both parties have the same variable, only

@ -178,7 +178,7 @@
* *
* The following transitions can occur while in the Bridge state: * The following transitions can occur while in the Bridge state:
* \li If a \ref ast_bridge_blob_type message indicating a leave is received, * \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 * \par Parked
* *
@ -203,27 +203,7 @@
* *
* The following transitions can occur while in the Parked state: * The following transitions can occur while in the Parked state:
* \li If a \ref ast_bridge_blob_type message indicating a leave is received, * \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 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.
* *
* \par Finalized * \par Finalized
* *

@ -927,6 +927,12 @@ enum {
* This flag indicates that the channel was originated. * This flag indicates that the channel was originated.
*/ */
AST_FLAG_ORIGINATED = (1 << 23), 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 */ /*! \brief ast_bridge_config flags */
@ -1018,8 +1024,12 @@ enum {
* instead of actually hanging up. * instead of actually hanging up.
*/ */
AST_SOFTHANGUP_UNBRIDGE = (1 << 6), 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. * \brief All softhangup flags.
* *

@ -68,6 +68,7 @@ struct ast_channel_snapshot {
int hangupcause; /*!< Why is the channel hanged up. See causes.h */ int hangupcause; /*!< Why is the channel hanged up. See causes.h */
int caller_pres; /*!< Caller ID presentation. */ int caller_pres; /*!< Caller ID presentation. */
struct ast_flags flags; /*!< channel flags of AST_FLAG_ type */ 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 */ struct varshead *manager_vars; /*!< Variables to be appended to manager events */
}; };

@ -120,12 +120,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</description> </description>
</configOption> </configOption>
<configOption name="endbeforehexten"> <configOption name="endbeforehexten">
<synopsis>End the CDR before executing the "h" extension</synopsis> <synopsis>Don't produce CDRs while executing hangup logic</synopsis>
<description><para>Normally, CDR's are not closed out until after all extensions are finished <description>
executing. By enabling this option, the CDR will be ended before executing <para>As each CDR for a channel is finished, its end time is updated
the <literal>h</literal> extension and hangup handlers so that CDR values such as <literal>end</literal> and and the CDR is finalized. When a channel is hung up and hangup
<literal>"billsec"</literal> may be retrieved inside of this extension. logic is present (in the form of a hangup handler or the
The default value is "no".</para> <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> </description>
</configOption> </configOption>
<configOption name="initiatedseconds"> <configOption name="initiatedseconds">
@ -335,6 +340,26 @@ static struct stasis_topic *cdr_topic;
struct cdr_object; 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. * \brief A virtual table used for \ref cdr_object.
* *
@ -425,11 +450,11 @@ struct cdr_object_fn_table {
* \param bridge The bridge that the Party A just entered into * \param bridge The bridge that the Party A just entered into
* \param channel The \ref ast_channel_snapshot for this CDR's Party A * \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 * \retval process_bridge_enter_results Defines whether or not this CDR was able
* was no Party B to find (we're all alone) * to fully handle the bridge enter message.
* \retval 1 This CDR couldn't find a Party B and channels were in the bridge
*/ */
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_bridge_snapshot *bridge,
struct ast_channel_snapshot *channel); struct ast_channel_snapshot *channel);
@ -476,6 +501,7 @@ struct cdr_object_fn_table {
}; };
static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot); 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_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_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); static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info);
@ -483,7 +509,7 @@ static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked
static void single_state_init_function(struct cdr_object *cdr); 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 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_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); 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 @@ struct cdr_object_fn_table single_state_fn_table = {
static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot); 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_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_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. * \brief The virtual table for the Dial state.
@ -540,7 +566,7 @@ struct cdr_object_fn_table dial_state_fn_table = {
static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot); 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_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); 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 @@ static int bridge_state_process_bridge_leave(struct cdr_object *cdr, struct ast_
* *
* A \ref cdr_object from this state can go to: * A \ref cdr_object from this state can go to:
* * \ref finalized_state_fn_table * * \ref finalized_state_fn_table
* * \ref pending_state_fn_table
*/ */
struct cdr_object_fn_table bridge_state_fn_table = { struct cdr_object_fn_table bridge_state_fn_table = {
.name = "Bridged", .name = "Bridged",
@ -592,36 +617,6 @@ struct cdr_object_fn_table bridge_state_fn_table = {
.process_parked_channel = base_process_parked_channel, .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); 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 @@ struct cdr_object_fn_table finalized_state_fn_table = {
.name = "Finalized", .name = "Finalized",
.init_function = finalized_state_init_function, .init_function = finalized_state_init_function,
.process_party_a = finalized_state_process_party_a, .process_party_a = finalized_state_process_party_a,
.process_bridge_enter = base_process_bridge_enter,
}; };
/*! \brief A wrapper object around a snapshot. /*! \brief A wrapper object around a snapshot.
@ -898,6 +894,38 @@ static struct cdr_object *cdr_object_create_and_append(struct cdr_object *cdr)
return new_cdr; return new_cdr;
} }
/*!
* \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 * \brief Return whether or not a \ref ast_channel_snapshot is for a channel
* that was created as the result of a dial operation * that was created as the result of a dial operation
@ -955,11 +983,7 @@ static struct cdr_object_snapshot *cdr_object_pick_party_a(struct cdr_object_sna
*/ */
static long cdr_object_get_duration(struct cdr_object *cdr) static long cdr_object_get_duration(struct cdr_object *cdr)
{ {
if (ast_tvzero(cdr->end)) { return (long)(ast_tvdiff_ms(ast_tvzero(cdr->end) ? ast_tvnow() : cdr->end, cdr->start) / 1000);
return (long)(ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
} else {
return (long)(ast_tvdiff_ms(cdr->end, cdr->start) / 1000);
}
} }
/*! /*!
@ -973,7 +997,7 @@ static long cdr_object_get_billsec(struct cdr_object *cdr)
if (ast_tvzero(cdr->answer)) { if (ast_tvzero(cdr->answer)) {
return 0; 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) if (ast_test_flag(&mod_cfg->general->settings, CDR_INITIATED_SECONDS)
&& (ms % 1000 >= 500)) { && (ms % 1000 >= 500)) {
ms = (ms / 1000) + 1; ms = (ms / 1000) + 1;
@ -984,6 +1008,33 @@ static long cdr_object_get_billsec(struct cdr_object *cdr)
return ms; return ms;
} }
/*!
* \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 * \brief Create a chain of \ref ast_cdr objects from a chain of \ref cdr_object
* suitable for consumption by the registered CDR backends * suitable for consumption by the registered CDR backends
@ -994,16 +1045,16 @@ static long cdr_object_get_billsec(struct cdr_object *cdr)
static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr) static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
{ {
struct ast_cdr *pub_cdr = NULL, *cdr_prev = NULL; 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_var_t *it_var, *it_copy_var;
struct ast_channel_snapshot *party_a; struct ast_channel_snapshot *party_a;
struct ast_channel_snapshot *party_b; struct ast_channel_snapshot *party_b;
while (cdr) { for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
struct ast_cdr *cdr_copy; struct ast_cdr *cdr_copy;
/* Don't create records for CDRs where the party A was a dialed channel */ /* Don't create records for CDRs where the party A was a dialed channel */
if (snapshot_is_dialed(cdr->party_a.snapshot)) { if (snapshot_is_dialed(it_cdr->party_a.snapshot)) {
cdr = cdr->next;
continue; continue;
} }
@ -1013,8 +1064,8 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
return NULL; return NULL;
} }
party_a = cdr->party_a.snapshot; party_a = it_cdr->party_a.snapshot;
party_b = cdr->party_b.snapshot; party_b = it_cdr->party_b.snapshot;
/* Party A */ /* Party A */
ast_assert(party_a != NULL); ast_assert(party_a != NULL);
@ -1024,8 +1075,8 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
ast_callerid_merge(cdr_copy->clid, sizeof(cdr_copy->clid), party_a->caller_name, party_a->caller_number, ""); 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->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->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->lastapp, it_cdr->appl, sizeof(cdr_copy->lastapp));
ast_copy_string(cdr_copy->lastdata, cdr->data, sizeof(cdr_copy->lastdata)); 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->dst, party_a->exten, sizeof(cdr_copy->dst));
ast_copy_string(cdr_copy->dcontext, party_a->context, sizeof(cdr_copy->dcontext)); ast_copy_string(cdr_copy->dcontext, party_a->context, sizeof(cdr_copy->dcontext));
@ -1033,30 +1084,30 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
if (party_b) { if (party_b) {
ast_copy_string(cdr_copy->dstchannel, party_b->name, sizeof(cdr_copy->dstchannel)); 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)); ast_copy_string(cdr_copy->peeraccount, party_b->accountcode, sizeof(cdr_copy->peeraccount));
if (!ast_strlen_zero(cdr->party_b.userfield)) { if (!ast_strlen_zero(it_cdr->party_b.userfield)) {
snprintf(cdr_copy->userfield, sizeof(cdr_copy->userfield), "%s;%s", cdr->party_a.userfield, 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)) { if (ast_strlen_zero(cdr_copy->userfield) && !ast_strlen_zero(it_cdr->party_a.userfield)) {
ast_copy_string(cdr_copy->userfield, cdr->party_a.userfield, sizeof(cdr_copy->userfield)); ast_copy_string(cdr_copy->userfield, it_cdr->party_a.userfield, sizeof(cdr_copy->userfield));
} }
/* Timestamps/durations */ /* Timestamps/durations */
cdr_copy->start = cdr->start; cdr_copy->start = it_cdr->start;
cdr_copy->answer = cdr->answer; cdr_copy->answer = it_cdr->answer;
cdr_copy->end = cdr->end; cdr_copy->end = it_cdr->end;
cdr_copy->billsec = cdr_object_get_billsec(cdr); cdr_copy->billsec = cdr_object_get_billsec(it_cdr);
cdr_copy->duration = cdr_object_get_duration(cdr); cdr_copy->duration = cdr_object_get_duration(it_cdr);
/* Flags and IDs */ /* Flags and IDs */
ast_copy_flags(cdr_copy, &cdr->flags, AST_FLAGS_ALL); ast_copy_flags(cdr_copy, &it_cdr->flags, AST_FLAGS_ALL);
ast_copy_string(cdr_copy->linkedid, cdr->linkedid, sizeof(cdr_copy->linkedid)); ast_copy_string(cdr_copy->linkedid, it_cdr->linkedid, sizeof(cdr_copy->linkedid));
cdr_copy->disposition = cdr->disposition; cdr_copy->disposition = it_cdr->disposition;
cdr_copy->sequence = cdr->sequence; cdr_copy->sequence = it_cdr->sequence;
/* Variables */ /* Variables */
copy_variables(&cdr_copy->varshead, &cdr->party_a.variables); copy_variables(&cdr_copy->varshead, &it_cdr->party_a.variables);
AST_LIST_TRAVERSE(&cdr->party_b.variables, it_var, entries) { AST_LIST_TRAVERSE(&it_cdr->party_b.variables, it_var, entries) {
int found = 0; int found = 0;
AST_LIST_TRAVERSE(&cdr_copy->varshead, it_copy_var, entries) { AST_LIST_TRAVERSE(&cdr_copy->varshead, it_copy_var, entries) {
if (!strcmp(ast_var_name(it_var), ast_var_name(it_copy_var))) { if (!strcmp(ast_var_name(it_var), ast_var_name(it_copy_var))) {
@ -1077,7 +1128,6 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr)
cdr_prev->next = cdr_copy; cdr_prev->next = cdr_copy;
cdr_prev = cdr_copy; cdr_prev = cdr_copy;
} }
cdr = cdr->next;
} }
return pub_cdr; return pub_cdr;
@ -1188,7 +1238,14 @@ static void cdr_object_finalize(struct cdr_object *cdr)
*/ */
static void cdr_object_check_party_a_hangup(struct cdr_object *cdr) 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->fn_table != &finalized_state_fn_table) {
cdr_object_transition_state(cdr, &finalized_state_fn_table); cdr_object_transition_state(cdr, &finalized_state_fn_table);
} }
@ -1210,35 +1267,6 @@ static void cdr_object_check_party_a_answer(struct cdr_object *cdr) {
} }
} }
/*!
* \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);
}
}
/* \brief Set Caller ID information on a CDR */ /* \brief Set Caller ID information on a CDR */
static void cdr_object_update_cid(struct cdr_object_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot) static void cdr_object_update_cid(struct cdr_object_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot)
{ {
@ -1318,6 +1346,12 @@ static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snap
return 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) static int base_process_parked_channel(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info)
{ {
char park_info[128]; char park_info[128];
@ -1424,12 +1458,12 @@ static int single_state_bridge_enter_comparison(struct cdr_object *cdr,
return 1; 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 ao2_iterator *it_cdrs;
struct cdr_object *cand_cdr_master; struct cdr_object *cand_cdr_master;
char *bridge_id = ast_strdupa(bridge->uniqueid); char *bridge_id = ast_strdupa(bridge->uniqueid);
int success = 1; int success = 0;
ast_string_field_set(cdr, bridge, bridge->uniqueid); ast_string_field_set(cdr, bridge, bridge->uniqueid);
@ -1439,7 +1473,7 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_
if (!it_cdrs) { if (!it_cdrs) {
/* No one in the bridge yet! */ /* No one in the bridge yet! */
cdr_object_transition_state(cdr, &bridge_state_fn_table); 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))) { while ((cand_cdr_master = ao2_iterator_next(it_cdrs))) {
@ -1458,7 +1492,7 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_
continue; continue;
} }
/* We successfully got a party B - break out */ /* We successfully got a party B - break out */
success = 0; success = 1;
break; break;
} }
ao2_unlock(cand_cdr_master); ao2_unlock(cand_cdr_master);
@ -1470,7 +1504,11 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_
cdr_object_transition_state(cdr, &bridge_state_fn_table); cdr_object_transition_state(cdr, &bridge_state_fn_table);
/* Success implies that we have a Party B */ /* 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) 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 @@ static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channe
cdr_object_swap_snapshot(&cdr->party_b, snapshot); cdr_object_swap_snapshot(&cdr->party_b, snapshot);
/* If party B hangs up, finalize this CDR */ /* 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); cdr_object_transition_state(cdr, &finalized_state_fn_table);
} }
} }
@ -1563,12 +1601,12 @@ static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channe
return 0; 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; struct ao2_iterator *it_cdrs;
char *bridge_id = ast_strdupa(bridge->uniqueid); char *bridge_id = ast_strdupa(bridge->uniqueid);
struct cdr_object *cand_cdr_master; struct cdr_object *cand_cdr_master;
int success = 1; int success = 0;
ast_string_field_set(cdr, bridge, bridge->uniqueid); ast_string_field_set(cdr, bridge, bridge->uniqueid);
@ -1578,7 +1616,7 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br
if (!it_cdrs) { if (!it_cdrs) {
/* No one in the bridge yet! */ /* No one in the bridge yet! */
cdr_object_transition_state(cdr, &bridge_state_fn_table); 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))) { while ((cand_cdr_master = ao2_iterator_next(it_cdrs))) {
@ -1610,7 +1648,7 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br
if (!cand_cdr->party_b.snapshot) { if (!cand_cdr->party_b.snapshot) {
cdr_object_finalize(cand_cdr); cdr_object_finalize(cand_cdr);
} }
success = 0; success = 1;
break; break;
} }
ao2_unlock(cand_cdr_master); ao2_unlock(cand_cdr_master);
@ -1622,7 +1660,10 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br
cdr_object_transition_state(cdr, &bridge_state_fn_table); cdr_object_transition_state(cdr, &bridge_state_fn_table);
/* Success implies that we have a Party B */ /* 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 */ /* DIALED PENDING STATE */
@ -1632,10 +1673,7 @@ static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct a
/* If we get a CEP change, we're executing dialplan. If we have a Party B /* 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. * that means we need a new CDR; otherwise, switch us over to single.
*/ */
if (strcmp(snapshot->context, cdr->party_a.snapshot->context) if (snapshot_cep_changed(cdr->party_a.snapshot, snapshot)) {
|| strcmp(snapshot->exten, cdr->party_a.snapshot->exten)
|| snapshot->priority != cdr->party_a.snapshot->priority
|| strcmp(snapshot->appl, cdr->party_a.snapshot->appl)) {
if (cdr->party_b.snapshot) { if (cdr->party_b.snapshot) {
cdr_object_transition_state(cdr, &finalized_state_fn_table); cdr_object_transition_state(cdr, &finalized_state_fn_table);
cdr->fn_table->process_party_a(cdr, snapshot); cdr->fn_table->process_party_a(cdr, snapshot);
@ -1650,7 +1688,7 @@ static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct a
return 0; 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); cdr_object_transition_state(cdr, &dial_state_fn_table);
return cdr->fn_table->process_bridge_enter(cdr, bridge, channel); return cdr->fn_table->process_bridge_enter(cdr, bridge, channel);
@ -1680,7 +1718,7 @@ static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_chan
cdr_object_swap_snapshot(&cdr->party_b, snapshot); cdr_object_swap_snapshot(&cdr->party_b, snapshot);
/* If party B hangs up, finalize this CDR */ /* 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); cdr_object_transition_state(cdr, &finalized_state_fn_table);
} }
} }
@ -1700,53 +1738,6 @@ static int bridge_state_process_bridge_leave(struct cdr_object *cdr, struct ast_
return 0; 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 */ /* PARKED STATE */
static int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel) 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 int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_
static void finalized_state_init_function(struct cdr_object *cdr) 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); cdr_object_finalize(cdr);
} }
static int finalized_state_process_party_a(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot) 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)) { RAII_VAR(struct module_config *, mod_cfg,
cdr_object_finalize(cdr); 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 */ /* Indicate that, if possible, we should get a new CDR */
@ -1968,7 +1958,7 @@ static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot,
return 0; return 0;
} }
if (ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE)) { if (ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD)) {
return 0; return 0;
} }
@ -1977,10 +1967,7 @@ static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot,
return 0; return 0;
} }
if (old_snapshot && !strcmp(old_snapshot->context, new_snapshot->context) if (old_snapshot && !snapshot_cep_changed(old_snapshot, new_snapshot)) {
&& !strcmp(old_snapshot->exten, new_snapshot->exten)
&& old_snapshot->priority == new_snapshot->priority
&& !(strcmp(old_snapshot->appl, new_snapshot->appl))) {
return 0; return 0;
} }
@ -2102,13 +2089,10 @@ static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, int flags)
if (strcmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) { if (strcmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) {
continue; continue;
} }
if (!it_cdr->fn_table->process_bridge_leave(it_cdr, leave_data->bridge, leave_data->channel)) { /* It is our Party B, in our bridge. Set the end time and let the handler
/* Update the end times for this CDR. We don't want to actually * transition our CDR appropriately when we leave the bridge.
* finalize it, as the Party A will eventually need to leave, which */
* will switch the records to pending bridged. cdr_object_finalize(it_cdr);
*/
cdr_object_finalize(it_cdr);
}
} }
return 0; return 0;
} }
@ -2144,7 +2128,6 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY), ao2_find(active_cdrs_by_channel, channel->name, OBJ_KEY),
ao2_cleanup); ao2_cleanup);
struct cdr_object *it_cdr; struct cdr_object *it_cdr;
struct cdr_object *pending_cdr;
struct bridge_leave_data leave_data = { struct bridge_leave_data leave_data = {
.bridge = bridge, .bridge = bridge,
.channel = channel, .channel = channel,
@ -2182,14 +2165,6 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
ao2_unlock(cdr); ao2_unlock(cdr);
return; 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); ao2_unlock(cdr);
if (strcmp(bridge->subclass, "parking")) { if (strcmp(bridge->subclass, "parking")) {
@ -2518,8 +2493,9 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
{ {
RAII_VAR(struct module_config *, mod_cfg, RAII_VAR(struct module_config *, mod_cfg,
ao2_global_obj_ref(module_configs), ao2_cleanup); 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 *it_cdr;
struct cdr_object *new_cdr;
struct cdr_object *handled_cdr = NULL; struct cdr_object *handled_cdr = NULL;
ao2_lock(cdr); ao2_lock(cdr);
@ -2535,20 +2511,31 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
if (it_cdr->fn_table->process_bridge_enter) { if (it_cdr->fn_table->process_bridge_enter) {
CDR_DEBUG(mod_cfg, "%p - Processing bridge enter for %s\n", it_cdr, CDR_DEBUG(mod_cfg, "%p - Processing bridge enter for %s\n", it_cdr,
channel->name); channel->name);
res &= it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel); result = it_cdr->fn_table->process_bridge_enter(it_cdr, bridge, channel);
if (!res && !handled_cdr) { switch (result) {
handled_cdr = it_cdr; 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: /* Create the new matchings, but only for either:
* * The first CDR in the chain that handled it. This avoids issues with * * The first CDR in the chain that handled it. This avoids issues with
* forked CDRs. * forked CDRs.
@ -2556,10 +2543,18 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
* a CDR joined a bridge and it wasn't Party A for anyone. We still need * a CDR joined a bridge and it wasn't Party A for anyone. We still need
* to make pairings with everyone in the bridge. * to make pairings with everyone in the bridge.
*/ */
if (!handled_cdr) { if (handled_cdr) {
handled_cdr = cdr->last; 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);
}
} }
handle_bridge_pairings(handled_cdr, bridge);
ao2_unlock(cdr); ao2_unlock(cdr);
} }
@ -2869,10 +2864,11 @@ static int cdr_object_select_all_by_channel_cb(void *obj, void *arg, int flags)
} }
/* Read Only CDR variables */ /* Read Only CDR variables */
static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel", static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext",
"lastapp", "lastdata", "start", "answer", "end", "duration", "channel", "dstchannel", "lastapp", "lastdata", "start", "answer", "end", "duration",
"billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid", "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
"userfield", "sequence", NULL }; "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) int ast_cdr_setvar(const char *channel_name, const char *name, const char *value)
{ {
@ -3020,11 +3016,11 @@ int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size
ao2_lock(cdr); ao2_lock(cdr);
cdr_obj = cdr->last; cdr_obj = cdr->last;
if (cdr_object_format_property(cdr_obj, name, value, length)) { if (cdr_object_format_property(cdr_obj, name, value, length)) {
/* Property failed; attempt variable */ /* Property failed; attempt variable */
cdr_object_format_var_internal(cdr_obj, name, value, length); cdr_object_format_var_internal(cdr_obj, name, value, length);
} }
ao2_unlock(cdr); ao2_unlock(cdr);
return 0; return 0;
@ -3188,7 +3184,7 @@ static void post_cdr(struct ast_cdr *cdr)
if (!ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) && if (!ast_test_flag(&mod_cfg->general->settings, CDR_UNANSWERED) &&
cdr->disposition < AST_CDR_ANSWERED && cdr->disposition < AST_CDR_ANSWERED &&
(ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) { (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; continue;
} }
@ -3311,7 +3307,6 @@ int ast_cdr_fork(const char *channel_name, struct ast_flags *options)
/* If the last CDR in the chain is finalized, don't allow a fork - /* If the last CDR in the chain is finalized, don't allow a fork -
* things are already dying at this point * things are already dying at this point
*/ */
ast_log(AST_LOG_ERROR, "FARK\n");
return -1; return -1;
} }
@ -3655,13 +3650,15 @@ static void cli_show_channels(struct ast_cli_args *a)
answer_time = it_cdr->answer; 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)) { if (ast_tvzero(start_time)) {
ao2_ref(cdr, -1); ao2_ref(cdr, -1);
continue; continue;
} }
it_cdr = cdr->last; 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(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(answer_time, "%T", answer_time_buffer, sizeof(answer_time_buffer));
cdr_get_tv(end_time, "%T", end_time_buffer, sizeof(end_time_buffer)); cdr_get_tv(end_time, "%T", end_time_buffer, sizeof(end_time_buffer));
@ -3671,7 +3668,7 @@ static void cli_show_channels(struct ast_cli_args *a)
start_time_buffer, start_time_buffer,
answer_time_buffer, answer_time_buffer,
end_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); (long)ast_tvdiff_ms(end_time, start_time) / 1000);
ao2_ref(cdr, -1); ao2_ref(cdr, -1);
} }
@ -3829,18 +3826,32 @@ static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_
static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) 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) { switch (cmd) {
case CLI_INIT: case CLI_INIT:
e->command = "cdr submit"; e->command = "cdr submit";
e->usage = e->usage =
"Usage: cdr submit\n" "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; return NULL;
case CLI_GENERATE: case CLI_GENERATE:
return NULL; return NULL;
} }
if (a->argc > 2) if (a->argc > 2) {
return CLI_SHOWUSAGE; 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(); submit_unscheduled_batch();
ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n"); ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
@ -3848,11 +3859,12 @@ static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_
return CLI_SUCCESS; return CLI_SUCCESS;
} }
static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data"); static struct ast_cli_entry cli_commands[] = {
static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status"); AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data"),
static struct ast_cli_entry cli_show = AST_CLI_DEFINE(handle_cli_show, "Display CDRs"); AST_CLI_DEFINE(handle_cli_status, "Display the CDR status"),
static struct ast_cli_entry cli_debug = AST_CLI_DEFINE(handle_cli_debug, "Enable debugging"); AST_CLI_DEFINE(handle_cli_show, "Display active CDRs for channels"),
AST_CLI_DEFINE(handle_cli_debug, "Enable debugging in the CDR engine"),
};
/*! /*!
* \brief This dispatches *all* \ref cdr_objects. It should only be used during * \brief This dispatches *all* \ref cdr_objects. It should only be used during
@ -3882,7 +3894,6 @@ static void finalize_batch_mode(void)
pthread_join(cdr_thread, NULL); pthread_join(cdr_thread, NULL);
cdr_thread = AST_PTHREADT_NULL; cdr_thread = AST_PTHREADT_NULL;
ast_cond_destroy(&cdr_pending_cond); ast_cond_destroy(&cdr_pending_cond);
ast_cli_unregister(&cli_submit);
ast_cdr_engine_term(); ast_cdr_engine_term();
} }
@ -3934,9 +3945,7 @@ static void cdr_engine_shutdown(void)
ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_dispatch_all_cb, ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_dispatch_all_cb,
NULL); NULL);
finalize_batch_mode(); finalize_batch_mode();
ast_cli_unregister(&cli_status); ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
ast_cli_unregister(&cli_debug);
ast_cli_unregister(&cli_show);
ast_sched_context_destroy(sched); ast_sched_context_destroy(sched);
sched = NULL; sched = NULL;
ast_free(batch); ast_free(batch);
@ -3944,6 +3953,7 @@ static void cdr_engine_shutdown(void)
channel_subscription = stasis_unsubscribe_and_join(channel_subscription); channel_subscription = stasis_unsubscribe_and_join(channel_subscription);
bridge_subscription = stasis_unsubscribe_and_join(bridge_subscription); bridge_subscription = stasis_unsubscribe_and_join(bridge_subscription);
parking_subscription = stasis_unsubscribe_and_join(parking_subscription);
stasis_message_router_unsubscribe_and_join(stasis_router); stasis_message_router_unsubscribe_and_join(stasis_router);
aco_info_destroy(&cfg_info); aco_info_destroy(&cfg_info);
ao2_global_obj_release(module_configs); ao2_global_obj_release(module_configs);
@ -3962,7 +3972,6 @@ static void cdr_enable_batch_mode(struct ast_cdr_config *config)
ast_log(LOG_ERROR, "Unable to start CDR thread.\n"); ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
return; return;
} }
ast_cli_register(&cli_submit);
} }
/* Kill the currently scheduled item */ /* Kill the currently scheduled item */
@ -4046,9 +4055,7 @@ int ast_cdr_engine_init(void)
return -1; return -1;
} }
ast_cli_register(&cli_status); ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands));
ast_cli_register(&cli_debug);
ast_cli_register(&cli_show);
ast_register_atexit(cdr_engine_shutdown); ast_register_atexit(cdr_engine_shutdown);
mod_cfg = ao2_global_obj_ref(module_configs); mod_cfg = ao2_global_obj_ref(module_configs);

@ -1084,8 +1084,8 @@ static void cel_channel_state_change(
return; return;
} }
was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0; was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0; is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
if (!was_hungup && is_hungup) { if (!was_hungup && is_hungup) {
RAII_VAR(struct ast_str *, extra_str, ast_str_create(128), ast_free); RAII_VAR(struct ast_str *, extra_str, ast_str_create(128), ast_free);

@ -2360,6 +2360,8 @@ static void ast_channel_destructor(void *obj)
char device_name[AST_CHANNEL_NAME]; char device_name[AST_CHANNEL_NAME];
struct ast_callid *callid; struct ast_callid *callid;
ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEAD);
ast_channel_publish_snapshot(chan);
publish_cache_clear(chan); publish_cache_clear(chan);
ast_pbx_hangup_handler_destroy(chan); ast_pbx_hangup_handler_destroy(chan);
@ -2866,8 +2868,6 @@ int ast_hangup(struct ast_channel *chan)
ast_cc_offer(chan); ast_cc_offer(chan);
ast_channel_publish_snapshot(chan);
ast_channel_unref(chan); ast_channel_unref(chan);
return 0; return 0;

@ -276,6 +276,9 @@ static void channel_data_add_flags(struct ast_data *tree,
ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN)); ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_RUN));
ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS)); ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_WORKAROUNDS));
ast_data_add_bool(tree, "DISABLE_DEVSTATE_CACHE", ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE)); ast_data_add_bool(tree, "DISABLE_DEVSTATE_CACHE", ast_test_flag(ast_channel_flags(chan), AST_FLAG_DISABLE_DEVSTATE_CACHE));
ast_data_add_bool(tree, "BRIDGE_DUAL_REDIRECT_WAIT", ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT));
ast_data_add_bool(tree, "ORIGINATED", ast_test_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED));
ast_data_add_bool(tree, "DEAD", ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEAD));
} }
int ast_channel_data_add_structure(struct ast_data *tree, int ast_channel_data_add_structure(struct ast_data *tree,

@ -603,8 +603,8 @@ static struct ast_manager_event_blob *channel_state_change(
EVENT_FLAG_CALL, "Newchannel", NO_EXTRA_FIELDS); EVENT_FLAG_CALL, "Newchannel", NO_EXTRA_FIELDS);
} }
was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0; was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0; is_hungup = ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD) ? 1 : 0;
if (!was_hungup && is_hungup) { if (!was_hungup && is_hungup) {
return ast_manager_event_blob_create( return ast_manager_event_blob_create(
@ -639,7 +639,7 @@ static struct ast_manager_event_blob *channel_newexten(
} }
/* Ignore any updates if we're hungup */ /* Ignore any updates if we're hungup */
if (ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE)) { if (ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD)) {
return NULL; return NULL;
} }

@ -5692,7 +5692,7 @@ void ast_pbx_h_exten_run(struct ast_channel *chan, const char *context)
* Make sure that the channel is marked as hungup since we are * Make sure that the channel is marked as hungup since we are
* going to run the h exten on it. * going to run the h exten on it.
*/ */
ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD); ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
/* Save autoloop flag */ /* Save autoloop flag */
autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
@ -5765,7 +5765,7 @@ int ast_pbx_hangup_handler_run(struct ast_channel *chan)
* Make sure that the channel is marked as hungup since we are * Make sure that the channel is marked as hungup since we are
* going to run the hangup handlers on it. * going to run the hangup handlers on it.
*/ */
ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD); ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC);
for (;;) { for (;;) {
handlers = ast_channel_hangup_handlers(chan); handlers = ast_channel_hangup_handlers(chan);

@ -185,8 +185,9 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha
snapshot->priority = ast_channel_priority(chan); snapshot->priority = ast_channel_priority(chan);
snapshot->amaflags = ast_channel_amaflags(chan); snapshot->amaflags = ast_channel_amaflags(chan);
snapshot->hangupcause = ast_channel_hangupcause(chan); snapshot->hangupcause = ast_channel_hangupcause(chan);
snapshot->flags = *ast_channel_flags(chan); ast_copy_flags(&snapshot->flags, ast_channel_flags(chan), 0xFFFFFFFF);
snapshot->caller_pres = ast_party_id_presentation(&ast_channel_caller(chan)->id); snapshot->caller_pres = ast_party_id_presentation(&ast_channel_caller(chan)->id);
ast_set_flag(&snapshot->softhangup_flags, ast_channel_softhangup_internal_flag(chan));
snapshot->manager_vars = ast_channel_get_manager_vars(chan); snapshot->manager_vars = ast_channel_get_manager_vars(chan);

Loading…
Cancel
Save