Merge "cel: Ensure only one dial status per channel exists."

changes/05/3005/1
zuul 9 years ago committed by Gerrit Code Review
commit dcb6875428

@ -171,6 +171,13 @@ struct cel_linkedid {
/*! Container of channel references to a linkedid for CEL purposes. */
static AO2_GLOBAL_OBJ_STATIC(cel_linkedids);
struct cel_dialstatus {
/*! Uniqueid of the channel */
char uniqueid[AST_MAX_UNIQUEID];
/*! The dial status */
char dialstatus[0];
};
/*! \brief Destructor for cel_config */
static void cel_general_config_dtor(void *obj)
{
@ -373,20 +380,10 @@ static int cel_backend_cmp(void *obj, void *arg, int flags)
return CMP_MATCH;
}
static const char *get_caller_uniqueid(struct ast_multi_channel_blob *blob)
{
struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
if (!caller) {
return NULL;
}
return caller->uniqueid;
}
/*! \brief Hashing function for dialstatus container */
static int dialstatus_hash(const void *obj, int flags)
{
struct ast_multi_channel_blob *blob;
const struct cel_dialstatus *dialstatus;
const char *key;
switch (flags & OBJ_SEARCH_MASK) {
@ -394,8 +391,8 @@ static int dialstatus_hash(const void *obj, int flags)
key = obj;
break;
case OBJ_SEARCH_OBJECT:
blob = (void *) obj;
key = get_caller_uniqueid(blob);
dialstatus = obj;
key = dialstatus->uniqueid;
break;
default:
/* Hash can only work on something with a full key. */
@ -408,24 +405,24 @@ static int dialstatus_hash(const void *obj, int flags)
/*! \brief Comparator function for dialstatus container */
static int dialstatus_cmp(void *obj, void *arg, int flags)
{
struct ast_multi_channel_blob *object_left = obj;
struct ast_multi_channel_blob *object_right = arg;
struct cel_dialstatus *object_left = obj;
struct cel_dialstatus *object_right = arg;
const char *right_key = arg;
int cmp;
switch (flags & OBJ_SEARCH_MASK) {
case OBJ_SEARCH_OBJECT:
right_key = get_caller_uniqueid(object_right);
right_key = object_right->uniqueid;
/* Fall through */
case OBJ_SEARCH_KEY:
cmp = strcmp(get_caller_uniqueid(object_left), right_key);
cmp = strcmp(object_left->uniqueid, 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 = strncmp(get_caller_uniqueid(object_left), right_key, strlen(right_key));
cmp = strncmp(object_left->uniqueid, right_key, strlen(right_key));
break;
default:
/*
@ -959,16 +956,16 @@ typedef void (*cel_channel_snapshot_monitor)(
struct ast_channel_snapshot *old_snapshot,
struct ast_channel_snapshot *new_snapshot);
static struct ast_multi_channel_blob *get_dialstatus_blob(const char *uniqueid)
static struct cel_dialstatus *get_dialstatus(const char *uniqueid)
{
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
struct ast_multi_channel_blob *blob = NULL;
struct cel_dialstatus *dialstatus = NULL;
if (dial_statuses) {
blob = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
dialstatus = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK);
ao2_ref(dial_statuses, -1);
}
return blob;
return dialstatus;
}
static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname)
@ -1011,19 +1008,15 @@ static void cel_channel_state_change(
if (!was_hungup && is_hungup) {
struct ast_json *extra;
struct ast_multi_channel_blob *blob = get_dialstatus_blob(new_snapshot->uniqueid);
const char *dialstatus = "";
struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid);
if (blob && !ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) {
dialstatus = get_blob_variable(blob, "dialstatus");
}
extra = ast_json_pack("{s: i, s: s, s: s}",
"hangupcause", new_snapshot->hangupcause,
"hangupsource", new_snapshot->hangupsource,
"dialstatus", dialstatus);
"dialstatus", dialstatus ? dialstatus->dialstatus : "");
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
ast_json_unref(extra);
ao2_cleanup(blob);
ao2_cleanup(dialstatus);
return;
}
@ -1255,16 +1248,48 @@ static void cel_parking_cb(
}
}
static void save_dialstatus(struct ast_multi_channel_blob *blob)
static void save_dialstatus(struct ast_multi_channel_blob *blob, struct ast_channel_snapshot *snapshot)
{
struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store);
const char *dialstatus_string = get_blob_variable(blob, "dialstatus");
struct cel_dialstatus *dialstatus;
size_t dialstatus_string_len;
ast_assert(blob != NULL);
if (!dial_statuses || ast_strlen_zero(dialstatus_string)) {
ao2_cleanup(dial_statuses);
return;
}
if (dial_statuses) {
ao2_link(dial_statuses, blob);
dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY);
if (dialstatus) {
if (!strcasecmp(dialstatus_string, "ANSWER") && strcasecmp(dialstatus->dialstatus, "ANSWER")) {
/* In the case of an answer after we already have a dial status we give
* priority to the answer since the call was, well, answered. In the case of
* failure dial status results we simply let the first failure be the status.
*/
ao2_unlink(dial_statuses, dialstatus);
ao2_ref(dialstatus, -1);
} else {
ao2_ref(dialstatus, -1);
ao2_ref(dial_statuses, -1);
return;
}
}
dialstatus_string_len = strlen(dialstatus_string) + 1;
dialstatus = ao2_alloc_options(sizeof(*dialstatus) + dialstatus_string_len, NULL,
AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!dialstatus) {
ao2_ref(dial_statuses, -1);
return;
}
ast_copy_string(dialstatus->uniqueid, snapshot->uniqueid, sizeof(dialstatus->uniqueid));
ast_copy_string(dialstatus->dialstatus, dialstatus_string, dialstatus_string_len);
ao2_link(dial_statuses, dialstatus);
ao2_ref(dialstatus, -1);
ao2_ref(dial_statuses, -1);
}
static int is_valid_dialstatus(struct ast_multi_channel_blob *blob)
@ -1300,32 +1325,25 @@ static void cel_dial_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
struct ast_multi_channel_blob *blob = stasis_message_data(message);
struct ast_channel_snapshot *snapshot;
if (cel_filter_channel_snapshot(ast_multi_channel_blob_get_channel(blob, "caller"))) {
return;
}
if (!get_caller_uniqueid(blob)) {
snapshot = ast_multi_channel_blob_get_channel(blob, "caller");
if (!snapshot || cel_filter_channel_snapshot(snapshot)) {
return;
}
if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller");
struct ast_json *extra;
if (!caller) {
return;
}
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
if (extra) {
cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL);
cel_report_event(snapshot, AST_CEL_FORWARD, NULL, extra, NULL);
ast_json_unref(extra);
}
}
if (is_valid_dialstatus(blob)) {
save_dialstatus(blob);
save_dialstatus(blob, snapshot);
}
}

@ -1610,7 +1610,7 @@ AST_TEST_DEFINE(test_cel_dial_pickup)
ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "CANCEL");
HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER");
HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, "");
return AST_TEST_PASS;

Loading…
Cancel
Save