cdr.c: Add container to key off of Party B channel names.
The CDR performance gets worse the further it gets behind in processing stasis messages. One of the reasons is because of a n*m loop used when processing Party B information. * Added a new CDR container that is keyed to Party B so we don't need such a large loop when processing Party B information. NOTE: To reduce the size of the patch I deferred to another patch the renaming of the Party A active_cdrs_by_channel container to active_cdrs_master and renaming the container's hash and cmp functions appropriately. ASTERISK-27335 Change-Id: I0bf66e8868f8adaa4b5dcf9e682e34951c350249
This commit is contained in:
parent
3a2b132411
commit
fe1120cf88
311
main/cdr.c
311
main/cdr.c
|
@ -351,6 +351,9 @@ static ast_cond_t cdr_pending_cond;
|
||||||
/*! \brief A container of the active CDRs indexed by Party A channel id */
|
/*! \brief A container of the active CDRs indexed by Party A channel id */
|
||||||
static struct ao2_container *active_cdrs_by_channel;
|
static struct ao2_container *active_cdrs_by_channel;
|
||||||
|
|
||||||
|
/*! \brief A container of all active CDRs indexed by Party B channel name */
|
||||||
|
static struct ao2_container *active_cdrs_all;
|
||||||
|
|
||||||
/*! \brief Message router for stasis messages regarding channel state */
|
/*! \brief Message router for stasis messages regarding channel state */
|
||||||
static struct stasis_message_router *stasis_router;
|
static struct stasis_message_router *stasis_router;
|
||||||
|
|
||||||
|
@ -713,6 +716,7 @@ struct cdr_object {
|
||||||
AST_STRING_FIELD(data); /*!< The data for the last accepted application party A was in */
|
AST_STRING_FIELD(data); /*!< The data for the last accepted application party A was in */
|
||||||
AST_STRING_FIELD(context); /*!< The accepted context for Party A */
|
AST_STRING_FIELD(context); /*!< The accepted context for Party A */
|
||||||
AST_STRING_FIELD(exten); /*!< The accepted extension for Party A */
|
AST_STRING_FIELD(exten); /*!< The accepted extension for Party A */
|
||||||
|
AST_STRING_FIELD(party_b_name); /*!< Party B channel name. Cached here as it is the all CDRs container key */
|
||||||
);
|
);
|
||||||
struct cdr_object *next; /*!< The next CDR object in the chain */
|
struct cdr_object *next; /*!< The next CDR object in the chain */
|
||||||
struct cdr_object *last; /*!< The last CDR object in the chain */
|
struct cdr_object *last; /*!< The last CDR object in the chain */
|
||||||
|
@ -848,6 +852,110 @@ static int cdr_object_channel_cmp_fn(void *obj, void *arg, int flags)
|
||||||
return cmp ? 0 : CMP_MATCH;
|
return cmp ? 0 : CMP_MATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Hash function for all CDR container indexed by Party B channel name.
|
||||||
|
*/
|
||||||
|
static int cdr_all_hash_fn(const void *obj, const int flags)
|
||||||
|
{
|
||||||
|
const struct cdr_object *cdr;
|
||||||
|
const char *key;
|
||||||
|
|
||||||
|
switch (flags & OBJ_SEARCH_MASK) {
|
||||||
|
case OBJ_SEARCH_KEY:
|
||||||
|
key = obj;
|
||||||
|
break;
|
||||||
|
case OBJ_SEARCH_OBJECT:
|
||||||
|
cdr = obj;
|
||||||
|
key = cdr->party_b_name;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ast_assert(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ast_str_case_hash(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Comparison function for all CDR container indexed by Party B channel name.
|
||||||
|
*/
|
||||||
|
static int cdr_all_cmp_fn(void *obj, void *arg, int flags)
|
||||||
|
{
|
||||||
|
struct cdr_object *left = obj;
|
||||||
|
struct cdr_object *right = arg;
|
||||||
|
const char *right_key = arg;
|
||||||
|
int cmp;
|
||||||
|
|
||||||
|
switch (flags & OBJ_SEARCH_MASK) {
|
||||||
|
case OBJ_SEARCH_OBJECT:
|
||||||
|
right_key = right->party_b_name;
|
||||||
|
/* Fall through */
|
||||||
|
case OBJ_SEARCH_KEY:
|
||||||
|
cmp = strcasecmp(left->party_b_name, right_key);
|
||||||
|
break;
|
||||||
|
case OBJ_SEARCH_PARTIAL_KEY:
|
||||||
|
/*
|
||||||
|
* We could also use a partial key struct containing a length
|
||||||
|
* so strlen() does not get called for every comparison instead.
|
||||||
|
*/
|
||||||
|
cmp = strncasecmp(left->party_b_name, right_key, strlen(right_key));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Sort can only work on something with a full or partial key. */
|
||||||
|
ast_assert(0);
|
||||||
|
cmp = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return cmp ? 0 : CMP_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Relink the CDR because Party B's snapshot changed.
|
||||||
|
* \since 13.19.0
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void cdr_all_relink(struct cdr_object *cdr)
|
||||||
|
{
|
||||||
|
ao2_lock(active_cdrs_all);
|
||||||
|
if (cdr->party_b.snapshot) {
|
||||||
|
if (strcasecmp(cdr->party_b_name, cdr->party_b.snapshot->name)) {
|
||||||
|
ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
|
||||||
|
ast_string_field_set(cdr, party_b_name, cdr->party_b.snapshot->name);
|
||||||
|
ao2_link_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ao2_unlink_flags(active_cdrs_all, cdr, OBJ_NOLOCK);
|
||||||
|
ast_string_field_set(cdr, party_b_name, "");
|
||||||
|
}
|
||||||
|
ao2_unlock(active_cdrs_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Unlink the master CDR and chained records from the active_cdrs_all container.
|
||||||
|
* \since 13.19.0
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void cdr_all_unlink(struct cdr_object *cdr)
|
||||||
|
{
|
||||||
|
struct cdr_object *cur;
|
||||||
|
struct cdr_object *next;
|
||||||
|
|
||||||
|
ast_assert(cdr->is_root);
|
||||||
|
|
||||||
|
ao2_lock(active_cdrs_all);
|
||||||
|
for (cur = cdr->next; cur; cur = next) {
|
||||||
|
next = cur->next;
|
||||||
|
ao2_unlink_flags(active_cdrs_all, cur, OBJ_NOLOCK);
|
||||||
|
ast_string_field_set(cur, party_b_name, "");
|
||||||
|
}
|
||||||
|
ao2_unlock(active_cdrs_all);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief \ref cdr_object Destructor
|
* \brief \ref cdr_object Destructor
|
||||||
*/
|
*/
|
||||||
|
@ -1509,6 +1617,7 @@ static int single_state_process_dial_begin(struct cdr_object *cdr, struct ast_ch
|
||||||
CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
|
CDR_DEBUG("%p - Updated Party A %s snapshot\n", cdr,
|
||||||
cdr->party_a.snapshot->name);
|
cdr->party_a.snapshot->name);
|
||||||
cdr_object_swap_snapshot(&cdr->party_b, peer);
|
cdr_object_swap_snapshot(&cdr->party_b, peer);
|
||||||
|
cdr_all_relink(cdr);
|
||||||
CDR_DEBUG("%p - Updated Party B %s snapshot\n", cdr,
|
CDR_DEBUG("%p - Updated Party B %s snapshot\n", cdr,
|
||||||
cdr->party_b.snapshot->name);
|
cdr->party_b.snapshot->name);
|
||||||
|
|
||||||
|
@ -1556,6 +1665,7 @@ static int single_state_bridge_enter_comparison(struct cdr_object *cdr,
|
||||||
CDR_DEBUG("%p - Party A %s has new Party B %s\n",
|
CDR_DEBUG("%p - Party A %s has new Party B %s\n",
|
||||||
cdr, cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name);
|
cdr, cdr->party_a.snapshot->name, cand_cdr->party_a.snapshot->name);
|
||||||
cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
|
cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_a);
|
||||||
|
cdr_all_relink(cdr);
|
||||||
if (!cand_cdr->party_b.snapshot) {
|
if (!cand_cdr->party_b.snapshot) {
|
||||||
/* We just stole them - finalize their CDR. Note that this won't
|
/* We just stole them - finalize their CDR. Note that this won't
|
||||||
* transition their state, it just sets the end time and the
|
* transition their state, it just sets the end time and the
|
||||||
|
@ -1576,6 +1686,7 @@ static int single_state_bridge_enter_comparison(struct cdr_object *cdr,
|
||||||
CDR_DEBUG("%p - Party A %s has new Party B %s\n",
|
CDR_DEBUG("%p - Party A %s has new Party B %s\n",
|
||||||
cdr, cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name);
|
cdr, cdr->party_a.snapshot->name, cand_cdr->party_b.snapshot->name);
|
||||||
cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_b);
|
cdr_object_snapshot_copy(&cdr->party_b, &cand_cdr->party_b);
|
||||||
|
cdr_all_relink(cdr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1728,8 +1839,6 @@ static int dial_state_process_dial_end(struct cdr_object *cdr, struct ast_channe
|
||||||
|
|
||||||
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)
|
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 *channel_id;
|
|
||||||
int success = 0;
|
int success = 0;
|
||||||
|
|
||||||
ast_string_field_set(cdr, bridge, bridge->uniqueid);
|
ast_string_field_set(cdr, bridge, bridge->uniqueid);
|
||||||
|
@ -1741,6 +1850,11 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct
|
||||||
return BRIDGE_ENTER_ONLY_PARTY;
|
return BRIDGE_ENTER_ONLY_PARTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we don't have a Party B (originated channel), skip it */
|
||||||
|
if (cdr->party_b.snapshot) {
|
||||||
|
struct ao2_iterator it_cdrs;
|
||||||
|
char *channel_id;
|
||||||
|
|
||||||
for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
|
for (it_cdrs = ao2_iterator_init(bridge->channels, 0);
|
||||||
!success && (channel_id = ao2_iterator_next(&it_cdrs));
|
!success && (channel_id = ao2_iterator_next(&it_cdrs));
|
||||||
ao2_ref(channel_id, -1)) {
|
ao2_ref(channel_id, -1)) {
|
||||||
|
@ -1756,13 +1870,8 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct
|
||||||
for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
|
for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->next) {
|
||||||
/* Skip any records that are not in a bridge or in this bridge.
|
/* Skip any records that are not in a bridge or in this bridge.
|
||||||
* I'm not sure how that would happen, but it pays to be careful. */
|
* I'm not sure how that would happen, but it pays to be careful. */
|
||||||
if (cand_cdr->fn_table != &bridge_state_fn_table ||
|
if (cand_cdr->fn_table != &bridge_state_fn_table
|
||||||
strcmp(cdr->bridge, cand_cdr->bridge)) {
|
|| strcmp(cdr->bridge, cand_cdr->bridge)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we don't have a Party B (originated channel), skip it */
|
|
||||||
if (!cdr->party_b.snapshot) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1785,6 +1894,7 @@ static enum process_bridge_enter_results dial_state_process_bridge_enter(struct
|
||||||
ao2_cleanup(cand_cdr_master);
|
ao2_cleanup(cand_cdr_master);
|
||||||
}
|
}
|
||||||
ao2_iterator_destroy(&it_cdrs);
|
ao2_iterator_destroy(&it_cdrs);
|
||||||
|
}
|
||||||
|
|
||||||
/* We always transition state, even if we didn't get a peer */
|
/* We always transition state, even if we didn't get a peer */
|
||||||
cdr_object_transition_state(cdr, &bridge_state_fn_table);
|
cdr_object_transition_state(cdr, &bridge_state_fn_table);
|
||||||
|
@ -2032,38 +2142,54 @@ static void handle_dial_message(void *data, struct stasis_subscription *sub, str
|
||||||
ao2_cleanup(cdr);
|
ao2_cleanup(cdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cdr_object_finalize_party_b(void *obj, void *arg, int flags)
|
static int cdr_object_finalize_party_b(void *obj, void *arg, void *data, int flags)
|
||||||
{
|
{
|
||||||
struct cdr_object *cdr = obj;
|
struct cdr_object *cdr = obj;
|
||||||
struct ast_channel_snapshot *party_b = arg;
|
|
||||||
struct cdr_object *it_cdr;
|
|
||||||
|
|
||||||
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
if (!strcasecmp(cdr->party_b_name, arg)) {
|
||||||
if (it_cdr->party_b.snapshot
|
#ifdef AST_DEVMODE
|
||||||
&& !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
|
struct ast_channel_snapshot *party_b = data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For sanity's sake we also assert the party_b snapshot
|
||||||
|
* is consistent with the key.
|
||||||
|
*/
|
||||||
|
ast_assert(cdr->party_b.snapshot
|
||||||
|
&& !strcasecmp(cdr->party_b.snapshot->name, party_b->name));
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Don't transition to the finalized state - let the Party A do
|
/* Don't transition to the finalized state - let the Party A do
|
||||||
* that when its ready
|
* that when its ready
|
||||||
*/
|
*/
|
||||||
cdr_object_finalize(it_cdr);
|
cdr_object_finalize(cdr);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cdr_object_update_party_b(void *obj, void *arg, int flags)
|
static int cdr_object_update_party_b(void *obj, void *arg, void *data, int flags)
|
||||||
{
|
{
|
||||||
struct cdr_object *cdr = obj;
|
struct cdr_object *cdr = obj;
|
||||||
struct ast_channel_snapshot *party_b = arg;
|
|
||||||
struct cdr_object *it_cdr;
|
|
||||||
|
|
||||||
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
if (cdr->fn_table->process_party_b
|
||||||
if (!it_cdr->fn_table->process_party_b) {
|
&& !strcasecmp(cdr->party_b_name, arg)) {
|
||||||
continue;
|
struct ast_channel_snapshot *party_b = data;
|
||||||
}
|
|
||||||
if (it_cdr->party_b.snapshot
|
/*
|
||||||
&& !strcasecmp(it_cdr->party_b.snapshot->name, party_b->name)) {
|
* For sanity's sake we also check the party_b snapshot
|
||||||
it_cdr->fn_table->process_party_b(it_cdr, party_b);
|
* for consistency with the key. The callback needs and
|
||||||
|
* asserts the snapshot to be this way.
|
||||||
|
*/
|
||||||
|
if (!cdr->party_b.snapshot
|
||||||
|
|| strcasecmp(cdr->party_b.snapshot->name, party_b->name)) {
|
||||||
|
ast_log(LOG_NOTICE,
|
||||||
|
"CDR for Party A %s(%s) has inconsistent Party B %s name. Message can be ignored but this shouldn't happen.\n",
|
||||||
|
cdr->linkedid,
|
||||||
|
cdr->party_a.snapshot->name,
|
||||||
|
cdr->party_b_name);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cdr->fn_table->process_party_b(cdr, party_b);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2137,11 +2263,10 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
|
||||||
name = new_snapshot ? new_snapshot->name : old_snapshot->name;
|
name = new_snapshot ? new_snapshot->name : old_snapshot->name;
|
||||||
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);
|
ast_log(AST_LOG_WARNING, "No CDR for channel %s\n", name);
|
||||||
ast_assert(0);
|
ast_assert(0);
|
||||||
} else {
|
} else if (new_snapshot) {
|
||||||
ao2_lock(cdr);
|
|
||||||
if (new_snapshot) {
|
|
||||||
int all_reject = 1;
|
int all_reject = 1;
|
||||||
|
|
||||||
|
ao2_lock(cdr);
|
||||||
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
||||||
if (!it_cdr->fn_table->process_party_a) {
|
if (!it_cdr->fn_table->process_party_a) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -2157,24 +2282,27 @@ static void handle_channel_cache_message(void *data, struct stasis_subscription
|
||||||
new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
|
new_cdr->fn_table->process_party_a(new_cdr, new_snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ao2_unlock(cdr);
|
||||||
} else {
|
} else {
|
||||||
|
ao2_lock(cdr);
|
||||||
CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
|
CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, old_snapshot->name);
|
||||||
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
||||||
cdr_object_finalize(it_cdr);
|
cdr_object_finalize(it_cdr);
|
||||||
}
|
}
|
||||||
cdr_object_dispatch(cdr);
|
cdr_object_dispatch(cdr);
|
||||||
ao2_unlink(active_cdrs_by_channel, cdr);
|
|
||||||
}
|
|
||||||
ao2_unlock(cdr);
|
ao2_unlock(cdr);
|
||||||
|
|
||||||
|
cdr_all_unlink(cdr);
|
||||||
|
ao2_unlink(active_cdrs_by_channel, cdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Party B */
|
/* Handle Party B */
|
||||||
if (new_snapshot) {
|
if (new_snapshot) {
|
||||||
ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_update_party_b,
|
ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
|
||||||
new_snapshot);
|
cdr_object_update_party_b, (char *) new_snapshot->name, new_snapshot);
|
||||||
} else {
|
} else {
|
||||||
ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_finalize_party_b,
|
ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
|
||||||
old_snapshot);
|
cdr_object_finalize_party_b, (char *) old_snapshot->name, old_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
ao2_cleanup(cdr);
|
ao2_cleanup(cdr);
|
||||||
|
@ -2186,29 +2314,25 @@ struct bridge_leave_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief Callback used to notify CDRs of a Party B leaving the bridge */
|
/*! \brief Callback used to notify CDRs of a Party B leaving the bridge */
|
||||||
static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, int flags)
|
static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, void *data, int flags)
|
||||||
{
|
{
|
||||||
struct cdr_object *cdr = obj;
|
struct cdr_object *cdr = obj;
|
||||||
struct bridge_leave_data *leave_data = arg;
|
struct bridge_leave_data *leave_data = data;
|
||||||
struct cdr_object *it_cdr;
|
|
||||||
|
if (cdr->fn_table == &bridge_state_fn_table
|
||||||
|
&& !strcmp(cdr->bridge, leave_data->bridge->uniqueid)
|
||||||
|
&& !strcasecmp(cdr->party_b_name, arg)) {
|
||||||
|
/*
|
||||||
|
* For sanity's sake we also assert the party_b snapshot
|
||||||
|
* is consistent with the key.
|
||||||
|
*/
|
||||||
|
ast_assert(cdr->party_b.snapshot
|
||||||
|
&& !strcasecmp(cdr->party_b.snapshot->name, leave_data->channel->name));
|
||||||
|
|
||||||
if (strcmp(cdr->bridge, leave_data->bridge->uniqueid)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
|
||||||
if (it_cdr->fn_table != &bridge_state_fn_table) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!it_cdr->party_b.snapshot) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strcasecmp(it_cdr->party_b.snapshot->name, leave_data->channel->name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* It is our Party B, in our bridge. Set the end time and let the handler
|
/* 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.
|
* transition our CDR appropriately when we leave the bridge.
|
||||||
*/
|
*/
|
||||||
cdr_object_finalize(it_cdr);
|
cdr_object_finalize(cdr);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2284,8 +2408,8 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
|
||||||
/* Party B */
|
/* Party B */
|
||||||
if (left_bridge
|
if (left_bridge
|
||||||
&& strcmp(bridge->subclass, "parking")) {
|
&& strcmp(bridge->subclass, "parking")) {
|
||||||
ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
|
ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
|
||||||
cdr_object_party_b_left_bridge_cb,
|
cdr_object_party_b_left_bridge_cb, (char *) leave_data.channel->name,
|
||||||
&leave_data);
|
&leave_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2308,6 +2432,7 @@ static void bridge_candidate_add_to_cdr(struct cdr_object *cdr,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cdr_object_snapshot_copy(&new_cdr->party_b, party_b);
|
cdr_object_snapshot_copy(&new_cdr->party_b, party_b);
|
||||||
|
cdr_all_relink(new_cdr);
|
||||||
cdr_object_check_party_a_answer(new_cdr);
|
cdr_object_check_party_a_answer(new_cdr);
|
||||||
ast_string_field_set(new_cdr, bridge, cdr->bridge);
|
ast_string_field_set(new_cdr, bridge, cdr->bridge);
|
||||||
cdr_object_transition_state(new_cdr, &bridge_state_fn_table);
|
cdr_object_transition_state(new_cdr, &bridge_state_fn_table);
|
||||||
|
@ -2366,6 +2491,7 @@ static int bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *b
|
||||||
cand_cdr, cand_cdr->party_a.snapshot->name,
|
cand_cdr, cand_cdr->party_a.snapshot->name,
|
||||||
cdr->party_a.snapshot->name);
|
cdr->party_a.snapshot->name);
|
||||||
cdr_object_snapshot_copy(&cand_cdr->party_b, &cdr->party_a);
|
cdr_object_snapshot_copy(&cand_cdr->party_b, &cdr->party_a);
|
||||||
|
cdr_all_relink(cand_cdr);
|
||||||
/* It's possible that this joined at one point and was never chosen
|
/* It's possible that this joined at one point and was never chosen
|
||||||
* as party A. Clear their end time, as it would be set in such a
|
* as party A. Clear their end time, as it would be set in such a
|
||||||
* case.
|
* case.
|
||||||
|
@ -3262,21 +3388,24 @@ struct party_b_userfield_update {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief Callback used to update the userfield on Party B on all CDRs */
|
/*! \brief Callback used to update the userfield on Party B on all CDRs */
|
||||||
static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, int flags)
|
static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, void *data, int flags)
|
||||||
{
|
{
|
||||||
struct cdr_object *cdr = obj;
|
struct cdr_object *cdr = obj;
|
||||||
struct party_b_userfield_update *info = arg;
|
|
||||||
struct cdr_object *it_cdr;
|
|
||||||
|
|
||||||
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
|
if ((cdr->fn_table != &finalized_state_fn_table || !cdr->next)
|
||||||
if (it_cdr->fn_table == &finalized_state_fn_table && it_cdr->next != NULL) {
|
&& !strcasecmp(cdr->party_b_name, arg)) {
|
||||||
continue;
|
struct party_b_userfield_update *info = data;
|
||||||
}
|
|
||||||
if (it_cdr->party_b.snapshot
|
/*
|
||||||
&& !strcasecmp(it_cdr->party_b.snapshot->name, info->channel_name)) {
|
* For sanity's sake we also assert the party_b snapshot
|
||||||
strcpy(it_cdr->party_b.userfield, info->userfield);
|
* is consistent with the key.
|
||||||
}
|
*/
|
||||||
|
ast_assert(cdr->party_b.snapshot
|
||||||
|
&& !strcasecmp(cdr->party_b.snapshot->name, info->channel_name));
|
||||||
|
|
||||||
|
strcpy(cdr->party_b.userfield, info->userfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3303,8 +3432,8 @@ void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle Party B */
|
/* Handle Party B */
|
||||||
ao2_callback(active_cdrs_by_channel, OBJ_NODATA,
|
ao2_callback_data(active_cdrs_all, OBJ_NODATA | OBJ_MULTIPLE | OBJ_SEARCH_KEY,
|
||||||
cdr_object_update_party_b_userfield_cb,
|
cdr_object_update_party_b_userfield_cb, (char *) party_b_info.channel_name,
|
||||||
&party_b_info);
|
&party_b_info);
|
||||||
|
|
||||||
ao2_cleanup(cdr);
|
ao2_cleanup(cdr);
|
||||||
|
@ -3485,6 +3614,7 @@ int ast_cdr_fork(const char *channel_name, struct ast_flags *options)
|
||||||
if (cdr_obj->party_b.snapshot) {
|
if (cdr_obj->party_b.snapshot) {
|
||||||
new_cdr->party_b.snapshot = cdr_obj->party_b.snapshot;
|
new_cdr->party_b.snapshot = cdr_obj->party_b.snapshot;
|
||||||
ao2_ref(new_cdr->party_b.snapshot, +1);
|
ao2_ref(new_cdr->party_b.snapshot, +1);
|
||||||
|
cdr_all_relink(new_cdr);
|
||||||
strcpy(new_cdr->party_b.userfield, cdr_obj->party_b.userfield);
|
strcpy(new_cdr->party_b.userfield, cdr_obj->party_b.userfield);
|
||||||
new_cdr->party_b.flags = cdr_obj->party_b.flags;
|
new_cdr->party_b.flags = cdr_obj->party_b.flags;
|
||||||
if (ast_test_flag(options, AST_CDR_FLAG_KEEP_VARS)) {
|
if (ast_test_flag(options, AST_CDR_FLAG_KEEP_VARS)) {
|
||||||
|
@ -4074,7 +4204,9 @@ static int cdr_object_dispatch_all_cb(void *obj, void *arg, int flags)
|
||||||
cdr_object_dispatch(cdr);
|
cdr_object_dispatch(cdr);
|
||||||
ao2_unlock(cdr);
|
ao2_unlock(cdr);
|
||||||
|
|
||||||
return 0;
|
cdr_all_unlink(cdr);
|
||||||
|
|
||||||
|
return CMP_MATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finalize_batch_mode(void)
|
static void finalize_batch_mode(void)
|
||||||
|
@ -4200,8 +4332,8 @@ static void cdr_engine_shutdown(void)
|
||||||
|
|
||||||
STASIS_MESSAGE_TYPE_CLEANUP(cdr_sync_message_type);
|
STASIS_MESSAGE_TYPE_CLEANUP(cdr_sync_message_type);
|
||||||
|
|
||||||
ao2_callback(active_cdrs_by_channel, OBJ_NODATA, cdr_object_dispatch_all_cb,
|
ao2_callback(active_cdrs_by_channel, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
|
||||||
NULL);
|
cdr_object_dispatch_all_cb, NULL);
|
||||||
finalize_batch_mode();
|
finalize_batch_mode();
|
||||||
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
|
ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands));
|
||||||
ast_sched_context_destroy(sched);
|
ast_sched_context_destroy(sched);
|
||||||
|
@ -4213,8 +4345,12 @@ static void cdr_engine_shutdown(void)
|
||||||
ao2_global_obj_release(module_configs);
|
ao2_global_obj_release(module_configs);
|
||||||
|
|
||||||
ao2_container_unregister("cdrs_by_channel");
|
ao2_container_unregister("cdrs_by_channel");
|
||||||
ao2_ref(active_cdrs_by_channel, -1);
|
ao2_cleanup(active_cdrs_by_channel);
|
||||||
active_cdrs_by_channel = NULL;
|
active_cdrs_by_channel = NULL;
|
||||||
|
|
||||||
|
ao2_container_unregister("cdrs_all");
|
||||||
|
ao2_cleanup(active_cdrs_all);
|
||||||
|
active_cdrs_all = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cdr_enable_batch_mode(struct ast_cdr_config *config)
|
static void cdr_enable_batch_mode(struct ast_cdr_config *config)
|
||||||
|
@ -4261,6 +4397,30 @@ static void cdr_container_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
* \brief Print all CDR container object.
|
||||||
|
* \since 13.19.0
|
||||||
|
*
|
||||||
|
* \param v_obj A pointer to the object we want printed.
|
||||||
|
* \param where User data needed by prnt to determine where to put output.
|
||||||
|
* \param prnt Print output callback function to use.
|
||||||
|
*
|
||||||
|
* \return Nothing
|
||||||
|
*/
|
||||||
|
static void cdr_all_print_fn(void *v_obj, void *where, ao2_prnt_fn *prnt)
|
||||||
|
{
|
||||||
|
struct cdr_object *cdr = v_obj;
|
||||||
|
|
||||||
|
if (!cdr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prnt(where, "Party A: %s; Party B: %s; Bridge %s",
|
||||||
|
cdr->party_a.snapshot->name,
|
||||||
|
cdr->party_b.snapshot ? cdr->party_b.snapshot->name : "<unknown>",
|
||||||
|
cdr->bridge);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Checks if CDRs are enabled and enables/disables the necessary options
|
* \brief Checks if CDRs are enabled and enables/disables the necessary options
|
||||||
*/
|
*/
|
||||||
|
@ -4327,6 +4487,13 @@ int ast_cdr_engine_init(void)
|
||||||
}
|
}
|
||||||
ao2_container_register("cdrs_by_channel", active_cdrs_by_channel, cdr_container_print_fn);
|
ao2_container_register("cdrs_by_channel", active_cdrs_by_channel, cdr_container_print_fn);
|
||||||
|
|
||||||
|
active_cdrs_all = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
|
||||||
|
NUM_CDR_BUCKETS, cdr_all_hash_fn, NULL, cdr_all_cmp_fn);
|
||||||
|
if (!active_cdrs_all) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ao2_container_register("cdrs_all", active_cdrs_all, cdr_all_print_fn);
|
||||||
|
|
||||||
sched = ast_sched_context_create();
|
sched = ast_sched_context_create();
|
||||||
if (!sched) {
|
if (!sched) {
|
||||||
ast_log(LOG_ERROR, "Unable to create schedule context.\n");
|
ast_log(LOG_ERROR, "Unable to create schedule context.\n");
|
||||||
|
|
Loading…
Reference in New Issue