struct timeval start; /*!< When this CDR was created */
struct timeval answer; /*!< Either when the channel was answered, or when the path between channels was established */
struct timeval end; /*!< When this CDR was finalized */
+ struct timeval lastevent; /*!< The time at which the last event was created regarding this CDR */
unsigned int sequence; /*!< A monotonically increasing number for each CDR */
struct ast_flags flags; /*!< Flags on the CDR */
AST_DECLARE_STRING_FIELDS(
* This implicitly sets the state of the newly created CDR to the Single state
* (\ref single_state_fn_table)
*/
-static struct cdr_object *cdr_object_alloc(struct ast_channel_snapshot *chan)
+static struct cdr_object *cdr_object_alloc(struct ast_channel_snapshot *chan, const struct timeval *event_time)
{
struct cdr_object *cdr;
ast_string_field_set(cdr, linkedid, chan->peer->linkedid);
cdr->disposition = AST_CDR_NULL;
cdr->sequence = ast_atomic_fetchadd_int(&global_cdr_sequence, +1);
+ cdr->lastevent = *event_time;
cdr->party_a.snapshot = chan;
ao2_t_ref(cdr->party_a.snapshot, +1, "bump snapshot during CDR creation");
* \brief Create a new \ref cdr_object and append it to an existing chain
* \param cdr The \ref cdr_object to append to
*/
-static struct cdr_object *cdr_object_create_and_append(struct cdr_object *cdr)
+static struct cdr_object *cdr_object_create_and_append(struct cdr_object *cdr, const struct timeval *event_time)
{
struct cdr_object *new_cdr;
struct cdr_object *it_cdr;
struct cdr_object *cdr_last;
cdr_last = cdr->last;
- new_cdr = cdr_object_alloc(cdr_last->party_a.snapshot);
+ new_cdr = cdr_object_alloc(cdr_last->party_a.snapshot, event_time);
if (!new_cdr) {
return NULL;
}
if (!ast_tvzero(cdr->end)) {
return;
}
- cdr->end = ast_tvnow();
+ cdr->end = cdr->lastevent;
if (cdr->disposition == AST_CDR_NULL) {
if (!ast_tvzero(cdr->answer)) {
static void cdr_object_check_party_a_answer(struct cdr_object *cdr)
{
if (cdr->party_a.snapshot->state == AST_STATE_UP && ast_tvzero(cdr->answer)) {
- cdr->answer = ast_tvnow();
+ cdr->answer = cdr->lastevent;
/* tv_usec is suseconds_t, which could be int or long */
CDR_DEBUG("%p - Set answered time to %ld.%06ld\n", cdr,
(long)cdr->answer.tv_sec,
static void single_state_init_function(struct cdr_object *cdr)
{
- cdr->start = ast_tvnow();
+ cdr->start = cdr->lastevent;
cdr_object_check_party_a_answer(cdr);
}
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *stasis_message_timestamp(message);
if (ast_strlen_zero(dial_status)) {
if (!it_cdr->fn_table->process_dial_begin) {
continue;
if (res && ast_strlen_zero(dial_status)) {
struct cdr_object *new_cdr;
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
if (new_cdr) {
new_cdr->fn_table->process_dial_begin(new_cdr, caller, peer);
}
}
if (update->new_snapshot && !update->old_snapshot) {
- cdr = cdr_object_alloc(update->new_snapshot);
+ cdr = cdr_object_alloc(update->new_snapshot, stasis_message_timestamp(message));
if (!cdr) {
return;
}
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *stasis_message_timestamp(message);
if (!it_cdr->fn_table->process_party_a) {
continue;
}
/* We're not hung up and we have a new snapshot - we need a new CDR */
struct cdr_object *new_cdr;
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
if (new_cdr) {
new_cdr->fn_table->process_party_a(new_cdr, update->new_snapshot);
}
ao2_lock(cdr);
CDR_DEBUG("%p - Beginning finalize/dispatch for %s\n", cdr, update->old_snapshot->base->name);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *stasis_message_timestamp(message);
cdr_object_finalize(it_cdr);
}
cdr_object_dispatch(cdr);
/* Party A */
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *stasis_message_timestamp(message);
if (!it_cdr->fn_table->process_bridge_leave) {
continue;
}
{
struct cdr_object *new_cdr;
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, &cdr->lastevent);
if (!new_cdr) {
return;
}
*/
static void handle_parking_bridge_enter_message(struct cdr_object *cdr,
struct ast_bridge_snapshot *bridge,
- struct ast_channel_snapshot *channel)
+ struct ast_channel_snapshot *channel,
+ const struct timeval *event_time)
{
int res = 1;
struct cdr_object *it_cdr;
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *event_time;
+
if (it_cdr->fn_table->process_parking_bridge_enter) {
res &= it_cdr->fn_table->process_parking_bridge_enter(it_cdr, bridge, channel);
}
if (res) {
/* No one handled it - we need a new one! */
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, event_time);
if (new_cdr) {
/* Let the single state transition us to Parked */
cdr_object_transition_state(new_cdr, &single_state_fn_table);
*/
static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
struct ast_bridge_snapshot *bridge,
- struct ast_channel_snapshot *channel)
+ struct ast_channel_snapshot *channel,
+ const struct timeval *event_time)
{
enum process_bridge_enter_results result;
struct cdr_object *it_cdr;
try_again:
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *event_time;
+
if (it_cdr->fn_table->process_party_a) {
CDR_DEBUG("%p - Updating Party A %s snapshot\n", it_cdr,
channel->base->name);
handle_bridge_pairings(handled_cdr, bridge);
} else {
/* Nothing handled it - we need a new one! */
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, event_time);
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
}
if (!strcmp(bridge->subclass, "parking")) {
- handle_parking_bridge_enter_message(cdr, bridge, channel);
+ handle_parking_bridge_enter_message(cdr, bridge, channel, stasis_message_timestamp(message));
} else {
- handle_standard_bridge_enter_message(cdr, bridge, channel);
+ handle_standard_bridge_enter_message(cdr, bridge, channel, stasis_message_timestamp(message));
}
ao2_cleanup(cdr);
}
ao2_lock(cdr);
for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->next) {
+ it_cdr->lastevent = *stasis_message_timestamp(message);
if (it_cdr->fn_table->process_parked_channel) {
unhandled &= it_cdr->fn_table->process_parked_channel(it_cdr, payload);
}
/* Nothing handled the messgae - we need a new one! */
struct cdr_object *new_cdr;
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, stasis_message_timestamp(message));
if (new_cdr) {
/* As the new CDR is created in the single state, it is guaranteed
* to have a function for the parked call message and will handle
memset(&it_cdr->end, 0, sizeof(it_cdr->end));
memset(&it_cdr->answer, 0, sizeof(it_cdr->answer));
it_cdr->start = ast_tvnow();
+ it_cdr->lastevent = it_cdr->start;
cdr_object_check_party_a_answer(it_cdr);
}
ao2_unlock(cdr);
{
SCOPED_AO2LOCK(lock, cdr);
+ struct timeval now = ast_tvnow();
cdr_obj = cdr->last;
if (cdr_obj->fn_table == &finalized_state_fn_table) {
* copied over automatically as part of the append
*/
ast_debug(1, "Forking CDR for channel %s\n", cdr->party_a.snapshot->base->name);
- new_cdr = cdr_object_create_and_append(cdr);
+ new_cdr = cdr_object_create_and_append(cdr, &now);
if (!new_cdr) {
return -1;
}
}
new_cdr->start = cdr_obj->start;
new_cdr->answer = cdr_obj->answer;
+ new_cdr->lastevent = ast_tvnow();
/* Modify the times based on the flags passed in */
if (ast_test_flag(options, AST_CDR_FLAG_SET_ANSWER)
struct ast_json *extra, const char *peer)
{
struct timeval eventtime = ast_tvnow();
+
+ return ast_cel_create_event_with_time(snapshot, event_type, &eventtime,
+ userdefevname, extra, peer);
+}
+
+struct ast_event *ast_cel_create_event_with_time(struct ast_channel_snapshot *snapshot,
+ enum ast_cel_event_type event_type, const struct timeval *event_time,
+ const char *userdefevname, struct ast_json *extra, const char *peer)
+{
RAII_VAR(char *, extra_txt, NULL, ast_json_free);
if (extra) {
extra_txt = ast_json_dump_string(extra);
}
return ast_event_new(AST_EVENT_CEL,
AST_EVENT_IE_CEL_EVENT_TYPE, AST_EVENT_IE_PLTYPE_UINT, event_type,
- AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_sec,
- AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, eventtime.tv_usec,
+ AST_EVENT_IE_CEL_EVENT_TIME, AST_EVENT_IE_PLTYPE_UINT, event_time->tv_sec,
+ AST_EVENT_IE_CEL_EVENT_TIME_USEC, AST_EVENT_IE_PLTYPE_UINT, event_time->tv_usec,
AST_EVENT_IE_CEL_USEREVENT_NAME, AST_EVENT_IE_PLTYPE_STR, S_OR(userdefevname, ""),
AST_EVENT_IE_CEL_CIDNAME, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->name,
AST_EVENT_IE_CEL_CIDNUM, AST_EVENT_IE_PLTYPE_STR, snapshot->caller->number,
}
static int cel_report_event(struct ast_channel_snapshot *snapshot,
- enum ast_cel_event_type event_type, const char *userdefevname,
- struct ast_json *extra, const char *peer_str)
+ enum ast_cel_event_type event_type, const struct timeval *event_time,
+ const char *userdefevname, struct ast_json *extra,
+ const char *peer_str)
{
struct ast_event *ev;
RAII_VAR(struct cel_config *, cfg, ao2_global_obj_ref(cel_configs), ao2_cleanup);
return 0;
}
- ev = ast_cel_create_event(snapshot, event_type, userdefevname, extra, peer_str);
+ ev = ast_cel_create_event_with_time(snapshot, event_type, event_time, userdefevname, extra, peer_str);
if (!ev) {
return -1;
}
/* called whenever a channel is destroyed or a linkedid is changed to
* potentially emit a CEL_LINKEDID_END event */
-static void check_retire_linkedid(struct ast_channel_snapshot *snapshot)
+static void check_retire_linkedid(struct ast_channel_snapshot *snapshot, const struct timeval *event_time)
{
RAII_VAR(struct ao2_container *, linkedids, ao2_global_obj_ref(cel_linkedids), ao2_cleanup);
struct cel_linkedid *lid;
ao2_unlink(linkedids, lid);
ao2_unlock(linkedids);
- cel_report_event(snapshot, AST_CEL_LINKEDID_END, NULL, NULL, NULL);
+ cel_report_event(snapshot, AST_CEL_LINKEDID_END, event_time, NULL, NULL, NULL);
} else {
ao2_unlock(linkedids);
}
/*! \brief Typedef for callbacks that get called on channel snapshot updates */
typedef void (*cel_channel_snapshot_monitor)(
struct ast_channel_snapshot *old_snapshot,
- struct ast_channel_snapshot *new_snapshot);
+ struct ast_channel_snapshot *new_snapshot,
+ const struct timeval *event_time);
static struct cel_dialstatus *get_dialstatus(const char *uniqueid)
{
/*! \brief Handle channel state changes */
static void cel_channel_state_change(
struct ast_channel_snapshot *old_snapshot,
- struct ast_channel_snapshot *new_snapshot)
+ struct ast_channel_snapshot *new_snapshot,
+ const struct timeval *event_time)
{
int is_hungup, was_hungup;
if (!old_snapshot) {
- cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, NULL, NULL, NULL);
+ cel_report_event(new_snapshot, AST_CEL_CHANNEL_START, event_time, NULL, NULL, NULL);
return;
}
"hangupcause", new_snapshot->hangup->cause,
"hangupsource", new_snapshot->hangup->source,
"dialstatus", dialstatus ? dialstatus->dialstatus : "");
- cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
+ cel_report_event(new_snapshot, AST_CEL_HANGUP, event_time, NULL, extra, NULL);
ast_json_unref(extra);
ao2_cleanup(dialstatus);
- cel_report_event(new_snapshot, AST_CEL_CHANNEL_END, NULL, NULL, NULL);
+ cel_report_event(new_snapshot, AST_CEL_CHANNEL_END, event_time, NULL, NULL, NULL);
if (ast_cel_track_event(AST_CEL_LINKEDID_END)) {
- check_retire_linkedid(new_snapshot);
+ check_retire_linkedid(new_snapshot, event_time);
}
return;
}
if (old_snapshot->state != new_snapshot->state && new_snapshot->state == AST_STATE_UP) {
- cel_report_event(new_snapshot, AST_CEL_ANSWER, NULL, NULL, NULL);
+ cel_report_event(new_snapshot, AST_CEL_ANSWER, event_time, NULL, NULL, NULL);
return;
}
}
static void cel_channel_linkedid_change(
struct ast_channel_snapshot *old_snapshot,
- struct ast_channel_snapshot *new_snapshot)
+ struct ast_channel_snapshot *new_snapshot,
+ const struct timeval *event_time)
{
if (!old_snapshot) {
return;
if (ast_cel_track_event(AST_CEL_LINKEDID_END)
&& strcmp(old_snapshot->peer->linkedid, new_snapshot->peer->linkedid)) {
cel_linkedid_ref(new_snapshot->peer->linkedid);
- check_retire_linkedid(old_snapshot);
+ check_retire_linkedid(old_snapshot, event_time);
}
}
static void cel_channel_app_change(
struct ast_channel_snapshot *old_snapshot,
- struct ast_channel_snapshot *new_snapshot)
+ struct ast_channel_snapshot *new_snapshot,
+ const struct timeval *event_time)
{
if (old_snapshot && !strcmp(old_snapshot->dialplan->appl, new_snapshot->dialplan->appl)) {
return;
/* old snapshot has an application, end it */
if (old_snapshot && !ast_strlen_zero(old_snapshot->dialplan->appl)) {
- cel_report_event(old_snapshot, AST_CEL_APP_END, NULL, NULL, NULL);
+ cel_report_event(old_snapshot, AST_CEL_APP_END, event_time, NULL, NULL, NULL);
}
/* new snapshot has an application, start it */
if (!ast_strlen_zero(new_snapshot->dialplan->appl)) {
- cel_report_event(new_snapshot, AST_CEL_APP_START, NULL, NULL, NULL);
+ cel_report_event(new_snapshot, AST_CEL_APP_START, event_time, NULL, NULL, NULL);
}
}
}
for (i = 0; i < ARRAY_LEN(cel_channel_monitors); ++i) {
- cel_channel_monitors[i](update->old_snapshot, update->new_snapshot);
+ cel_channel_monitors[i](update->old_snapshot, update->new_snapshot, stasis_message_timestamp(message));
}
}
return;
}
- cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, NULL, extra, ast_str_buffer(peer_str));
+ cel_report_event(chan_snapshot, AST_CEL_BRIDGE_ENTER, stasis_message_timestamp(message),
+ NULL, extra, ast_str_buffer(peer_str));
}
static void cel_bridge_leave_cb(
return;
}
- cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, NULL, extra, ast_str_buffer(peer_str));
+ cel_report_event(chan_snapshot, AST_CEL_BRIDGE_EXIT, stasis_message_timestamp(message),
+ NULL, extra, ast_str_buffer(peer_str));
}
static void cel_parking_cb(
"parker_dial_string", parked_payload->parker_dial_string,
"parking_lot", parked_payload->parkinglot);
if (extra) {
- cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, NULL, extra, NULL);
+ cel_report_event(parked_payload->parkee, AST_CEL_PARK_START, stasis_message_timestamp(message),
+ NULL, extra, NULL);
}
return;
case PARKED_CALL_TIMEOUT:
}
if (extra) {
- cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, NULL, extra, NULL);
+ cel_report_event(parked_payload->parkee, AST_CEL_PARK_END, stasis_message_timestamp(message),
+ NULL, extra, NULL);
}
}
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
if (extra) {
- cel_report_event(snapshot, AST_CEL_FORWARD, NULL, extra, NULL);
+ cel_report_event(snapshot, AST_CEL_FORWARD, stasis_message_timestamp(message),
+ NULL, extra, NULL);
ast_json_unref(extra);
}
}
{
const char *event = ast_json_string_get(ast_json_object_get(event_details, "event"));
struct ast_json *extra = ast_json_object_get(event_details, "extra");
- cel_report_event(obj->snapshot, event_type, event, extra, NULL);
+ cel_report_event(obj->snapshot, event_type, stasis_message_timestamp(message),
+ event, extra, NULL);
break;
}
default:
"transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->base->name : "N/A",
"transferee_channel_uniqueid", transfer_msg->transferee ? transfer_msg->transferee->base->uniqueid : "N/A");
if (extra) {
- cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
+ cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, stasis_message_timestamp(message),
+ NULL, extra, NULL);
ast_json_unref(extra);
}
}
}
break;
}
- cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, NULL, extra, NULL);
+ cel_report_event(channel1, AST_CEL_ATTENDEDTRANSFER, stasis_message_timestamp(message),
+ NULL, extra, NULL);
ast_json_unref(extra);
}
return;
}
- cel_report_event(target, AST_CEL_PICKUP, NULL, extra, NULL);
+ cel_report_event(target, AST_CEL_PICKUP, stasis_message_timestamp(message), NULL, extra, NULL);
ast_json_unref(extra);
}
return;
}
- cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, NULL, extra, NULL);
+ cel_report_event(localone, AST_CEL_LOCAL_OPTIMIZE, stasis_message_timestamp(message), NULL, extra, NULL);
ast_json_unref(extra);
}