diff --git a/CHANGES b/CHANGES index 3223822ba6..037001dafc 100644 --- a/CHANGES +++ b/CHANGES @@ -286,8 +286,11 @@ CDR (Call Detail Records) * 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 diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index cd0501f06f..49acc61dd2 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -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 * diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 0f55491086..a5f5210046 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -927,6 +927,12 @@ enum { * 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 @@ enum { * 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. * diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h index 90df75ec58..e8fc48606a 100644 --- a/include/asterisk/stasis_channels.h +++ b/include/asterisk/stasis_channels.h @@ -68,6 +68,7 @@ struct ast_channel_snapshot { 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 */ }; diff --git a/main/cdr.c b/main/cdr.c index 80887d1350..5fdd60c220 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -120,12 +120,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </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 @@ static struct stasis_topic *cdr_topic; 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 @@ struct cdr_object_fn_table { * \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 @@ struct cdr_object_fn_table { }; 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 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_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 @@ 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 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 @@ 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_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 @@ static int bridge_state_process_bridge_leave(struct cdr_object *cdr, struct ast_ * * 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 @@ struct cdr_object_fn_table bridge_state_fn_table = { .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 @@ struct cdr_object_fn_table finalized_state_fn_table = { .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. @@ -898,6 +894,38 @@ static struct cdr_object *cdr_object_create_and_append(struct cdr_object *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 * 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) { - 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 @@ static long cdr_object_get_billsec(struct cdr_object *cdr) 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; @@ -984,6 +1008,33 @@ static long cdr_object_get_billsec(struct cdr_object *cdr) 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 * 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) { 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 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr) 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 @@ 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_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 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr) 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 @@ static struct ast_cdr *cdr_object_create_public_records(struct cdr_object *cdr) cdr_prev->next = cdr_copy; cdr_prev = cdr_copy; } - cdr = cdr->next; } 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) { - 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); } @@ -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 */ 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; } +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) { char park_info[128]; @@ -1424,12 +1458,12 @@ static int single_state_bridge_enter_comparison(struct cdr_object *cdr, 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 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_ 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 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_ continue; } /* We successfully got a party B - break out */ - success = 0; + success = 1; break; } 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); /* 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 @@ static void dial_state_process_party_b(struct cdr_object *cdr, struct ast_channe 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 @@ static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channe 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 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br 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 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br 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 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br 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 @@ 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 * 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 @@ static int dialed_pending_state_process_party_a(struct cdr_object *cdr, struct a 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 @@ static void bridge_state_process_party_b(struct cdr_object *cdr, struct ast_chan 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 @@ static int bridge_state_process_bridge_leave(struct cdr_object *cdr, struct ast_ 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 int parked_state_process_bridge_leave(struct cdr_object *cdr, struct ast_ 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 @@ static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot, 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 @@ static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot, 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 @@ 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)) { 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 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription * 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, @@ -2182,14 +2165,6 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription * 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); 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, 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,20 +2511,31 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr, 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: * * The first CDR in the chain that handled it. This avoids issues with * 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 * to make pairings with everyone in the bridge. */ - if (!handled_cdr) { - handled_cdr = cdr->last; + 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); + } } - handle_bridge_pairings(handled_cdr, bridge); 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 */ -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 @@ int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size 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 @@ static void post_cdr(struct ast_cdr *cdr) 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 @@ 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 - * things are already dying at this point */ - ast_log(AST_LOG_ERROR, "FARK\n"); return -1; } @@ -3655,13 +3650,15 @@ static void cli_show_channels(struct ast_cli_args *a) 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 @@ static void cli_show_channels(struct ast_cli_args *a) 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_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) { + 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 @@ static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_ 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_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status"); -static struct ast_cli_entry cli_show = AST_CLI_DEFINE(handle_cli_show, "Display CDRs"); -static struct ast_cli_entry cli_debug = AST_CLI_DEFINE(handle_cli_debug, "Enable debugging"); - +static struct ast_cli_entry cli_commands[] = { + AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data"), + AST_CLI_DEFINE(handle_cli_status, "Display the CDR status"), + 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 @@ -3882,7 +3894,6 @@ static void finalize_batch_mode(void) pthread_join(cdr_thread, NULL); cdr_thread = AST_PTHREADT_NULL; ast_cond_destroy(&cdr_pending_cond); - ast_cli_unregister(&cli_submit); 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, NULL); finalize_batch_mode(); - ast_cli_unregister(&cli_status); - ast_cli_unregister(&cli_debug); - ast_cli_unregister(&cli_show); + ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); ast_sched_context_destroy(sched); sched = NULL; ast_free(batch); @@ -3944,6 +3953,7 @@ static void cdr_engine_shutdown(void) channel_subscription = stasis_unsubscribe_and_join(channel_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); aco_info_destroy(&cfg_info); 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"); return; } - ast_cli_register(&cli_submit); } /* Kill the currently scheduled item */ @@ -4046,9 +4055,7 @@ int ast_cdr_engine_init(void) return -1; } - ast_cli_register(&cli_status); - ast_cli_register(&cli_debug); - ast_cli_register(&cli_show); + ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); ast_register_atexit(cdr_engine_shutdown); mod_cfg = ao2_global_obj_ref(module_configs); diff --git a/main/cel.c b/main/cel.c index bc1182aa74..b71afde223 100644 --- a/main/cel.c +++ b/main/cel.c @@ -1084,8 +1084,8 @@ static void cel_channel_state_change( return; } - was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0; - is_hungup = ast_test_flag(&new_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_DEAD) ? 1 : 0; if (!was_hungup && is_hungup) { RAII_VAR(struct ast_str *, extra_str, ast_str_create(128), ast_free); diff --git a/main/channel.c b/main/channel.c index ce11e50b75..104558b16b 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2360,6 +2360,8 @@ static void ast_channel_destructor(void *obj) char device_name[AST_CHANNEL_NAME]; struct ast_callid *callid; + ast_set_flag(ast_channel_flags(chan), AST_FLAG_DEAD); + ast_channel_publish_snapshot(chan); publish_cache_clear(chan); ast_pbx_hangup_handler_destroy(chan); @@ -2866,8 +2868,6 @@ int ast_hangup(struct ast_channel *chan) ast_cc_offer(chan); - ast_channel_publish_snapshot(chan); - ast_channel_unref(chan); return 0; diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index afa4c9f3d3..085052a715 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -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, "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, "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, diff --git a/main/manager_channels.c b/main/manager_channels.c index c9e38dfbda..7dd91a63bb 100644 --- a/main/manager_channels.c +++ b/main/manager_channels.c @@ -603,8 +603,8 @@ static struct ast_manager_event_blob *channel_state_change( EVENT_FLAG_CALL, "Newchannel", NO_EXTRA_FIELDS); } - was_hungup = ast_test_flag(&old_snapshot->flags, AST_FLAG_ZOMBIE) ? 1 : 0; - is_hungup = ast_test_flag(&new_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_DEAD) ? 1 : 0; if (!was_hungup && is_hungup) { 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 */ - if (ast_test_flag(&new_snapshot->flags, AST_FLAG_ZOMBIE)) { + if (ast_test_flag(&new_snapshot->flags, AST_FLAG_DEAD)) { return NULL; } diff --git a/main/pbx.c b/main/pbx.c index 8079edc622..f3d91842c0 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -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 * 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 */ 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 * going to run the hangup handlers on it. */ - ast_softhangup_nolock(chan, AST_SOFTHANGUP_APPUNLOAD); + ast_softhangup_nolock(chan, AST_SOFTHANGUP_HANGUP_EXEC); for (;;) { handlers = ast_channel_hangup_handlers(chan); diff --git a/main/stasis_channels.c b/main/stasis_channels.c index 8cf259579b..9179878d59 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -185,8 +185,9 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha snapshot->priority = ast_channel_priority(chan); snapshot->amaflags = ast_channel_amaflags(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); + ast_set_flag(&snapshot->softhangup_flags, ast_channel_softhangup_internal_flag(chan)); snapshot->manager_vars = ast_channel_get_manager_vars(chan);