@ -318,9 +318,6 @@ static ast_cond_t cdr_pending_cond;
/*! \brief A container of the active CDRs indexed by Party A channel name */
/*! \brief A container of the active CDRs indexed by Party A channel name */
static struct ao2_container * active_cdrs_by_channel ;
static struct ao2_container * active_cdrs_by_channel ;
/*! \brief A container of the active CDRs indexed by the bridge ID */
static struct ao2_container * active_cdrs_by_bridge ;
/*! \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 ;
@ -783,16 +780,6 @@ static int cdr_object_channel_cmp_fn(void *obj, void *arg, int flags)
return strcasecmp ( left - > name , match ) ? 0 : ( CMP_MATCH | CMP_STOP ) ;
return strcasecmp ( left - > name , match ) ? 0 : ( CMP_MATCH | CMP_STOP ) ;
}
}
/*! \internal
* \ brief Hash function for containers of CDRs indexing by bridge ID
*/
static int cdr_object_bridge_hash_fn ( const void * obj , const int flags )
{
const struct cdr_object * cdr = obj ;
const char * id = ( flags & OBJ_KEY ) ? obj : cdr - > bridge ;
return ast_str_case_hash ( id ) ;
}
/*! \internal
/*! \internal
* \ brief Comparison function for containers of CDRs indexing by bridge . Note
* \ brief Comparison function for containers of CDRs indexing by bridge . Note
* that we expect there to be collisions , as a single bridge may have multiple
* that we expect there to be collisions , as a single bridge may have multiple
@ -801,9 +788,9 @@ static int cdr_object_bridge_hash_fn(const void *obj, const int flags)
static int cdr_object_bridge_cmp_fn ( void * obj , void * arg , int flags )
static int cdr_object_bridge_cmp_fn ( void * obj , void * arg , int flags )
{
{
struct cdr_object * left = obj ;
struct cdr_object * left = obj ;
struct cdr_object * right = arg ;
struct cdr_object * it_cdr ;
struct cdr_object * it_cdr ;
const char * match = ( flags & OBJ_KEY ) ? arg : right - > bridge ;
const char * match = arg ;
for ( it_cdr = left ; it_cdr ; it_cdr = it_cdr - > next ) {
for ( it_cdr = left ; it_cdr ; it_cdr = it_cdr - > next ) {
if ( ! strcasecmp ( it_cdr - > bridge , match ) ) {
if ( ! strcasecmp ( it_cdr - > bridge , match ) ) {
return CMP_MATCH ;
return CMP_MATCH ;
@ -1405,6 +1392,11 @@ static int single_state_bridge_enter_comparison(struct cdr_object *cdr,
{
{
struct cdr_object_snapshot * party_a ;
struct cdr_object_snapshot * party_a ;
/* Don't match on ourselves */
if ( ! strcmp ( cdr - > party_a . snapshot - > name , cand_cdr - > party_a . snapshot - > name ) ) {
return 1 ;
}
/* Try the candidate CDR's Party A first */
/* Try the candidate CDR's Party A first */
party_a = cdr_object_pick_party_a ( & cdr - > party_a , & cand_cdr - > party_a ) ;
party_a = cdr_object_pick_party_a ( & cdr - > party_a , & cand_cdr - > party_a ) ;
if ( ! strcmp ( party_a - > snapshot - > name , cdr - > party_a . snapshot - > name ) ) {
if ( ! strcmp ( party_a - > snapshot - > name , cdr - > party_a . snapshot - > name ) ) {
@ -1419,8 +1411,8 @@ static int single_state_bridge_enter_comparison(struct cdr_object *cdr,
return 0 ;
return 0 ;
}
}
/* Try their Party B */
/* Try their Party B , unless it's us */
if ( ! cand_cdr - > party_b . snapshot ) {
if ( ! cand_cdr - > party_b . snapshot | | ! strcmp ( cdr - > party_a . snapshot - > name , cand_cdr - > party_b . snapshot - > name ) ) {
return 1 ;
return 1 ;
}
}
party_a = cdr_object_pick_party_a ( & cdr - > party_a , & cand_cdr - > party_b ) ;
party_a = cdr_object_pick_party_a ( & cdr - > party_a , & cand_cdr - > party_b ) ;
@ -1442,7 +1434,7 @@ static int single_state_process_bridge_enter(struct cdr_object *cdr, struct ast_
ast_string_field_set ( cdr , bridge , bridge - > uniqueid ) ;
ast_string_field_set ( cdr , bridge , bridge - > uniqueid ) ;
/* Get parties in the bridge */
/* Get parties in the bridge */
it_cdrs = ao2_callback ( active_cdrs_by_ bridge, OBJ_MULTIPLE | OBJ_KEY ,
it_cdrs = ao2_callback ( active_cdrs_by_ channel, OBJ_MULTIPLE ,
cdr_object_bridge_cmp_fn , bridge_id ) ;
cdr_object_bridge_cmp_fn , bridge_id ) ;
if ( ! it_cdrs ) {
if ( ! it_cdrs ) {
/* No one in the bridge yet! */
/* No one in the bridge yet! */
@ -1581,7 +1573,7 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br
ast_string_field_set ( cdr , bridge , bridge - > uniqueid ) ;
ast_string_field_set ( cdr , bridge , bridge - > uniqueid ) ;
/* Get parties in the bridge */
/* Get parties in the bridge */
it_cdrs = ao2_callback ( active_cdrs_by_ bridge, OBJ_MULTIPLE | OBJ_KEY ,
it_cdrs = ao2_callback ( active_cdrs_by_ channel, OBJ_MULTIPLE ,
cdr_object_bridge_cmp_fn , bridge_id ) ;
cdr_object_bridge_cmp_fn , bridge_id ) ;
if ( ! it_cdrs ) {
if ( ! it_cdrs ) {
/* No one in the bridge yet! */
/* No one in the bridge yet! */
@ -1610,7 +1602,6 @@ static int dial_state_process_bridge_enter(struct cdr_object *cdr, struct ast_br
if ( strcmp ( cdr - > party_b . snapshot - > name , cand_cdr - > party_a . snapshot - > name ) ) {
if ( strcmp ( cdr - > party_b . snapshot - > name , cand_cdr - > party_a . snapshot - > name ) ) {
continue ;
continue ;
}
}
cdr_object_snapshot_copy ( & cdr - > party_b , & cand_cdr - > party_a ) ;
cdr_object_snapshot_copy ( & cdr - > party_b , & cand_cdr - > party_a ) ;
/* If they have a Party B, they joined up with someone else as their
/* If they have a Party B, they joined up with someone else as their
* Party A . Don ' t finalize them as they ' re active . Otherwise , we
* Party A . Don ' t finalize them as they ' re active . Otherwise , we
@ -2192,10 +2183,6 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
return ;
return ;
}
}
if ( strcmp ( bridge - > subclass , " parking " ) ) {
ao2_unlink ( active_cdrs_by_bridge , cdr ) ;
}
/* Create a new pending record. If the channel decides to do something else,
/* Create a new pending record. If the channel decides to do something else,
* the pending record will handle it - otherwise , if gets dropped .
* the pending record will handle it - otherwise , if gets dropped .
*/
*/
@ -2207,7 +2194,7 @@ static void handle_bridge_leave_message(void *data, struct stasis_subscription *
if ( strcmp ( bridge - > subclass , " parking " ) ) {
if ( strcmp ( bridge - > subclass , " parking " ) ) {
/* Party B */
/* Party B */
ao2_callback ( active_cdrs_by_ bridge , OBJ_NODATA ,
ao2_callback ( active_cdrs_by_ channel , OBJ_NODATA ,
cdr_object_party_b_left_bridge_cb ,
cdr_object_party_b_left_bridge_cb ,
& leave_data ) ;
& leave_data ) ;
}
}
@ -2338,7 +2325,7 @@ static struct ao2_container *create_candidates_for_bridge(struct ast_bridge_snap
/* For each CDR that has a record in the bridge, get their Party A and
/* For each CDR that has a record in the bridge, get their Party A and
* make them a candidate . Note that we do this in two passes as opposed to one so
* make them a candidate . Note that we do this in two passes as opposed to one so
* that we give preference CDRs where the channel is Party A */
* that we give preference CDRs where the channel is Party A */
it_cdrs = ao2_callback ( active_cdrs_by_ bridge, OBJ_MULTIPLE | OBJ_KEY ,
it_cdrs = ao2_callback ( active_cdrs_by_ channel, OBJ_MULTIPLE ,
cdr_object_bridge_cmp_fn , bridge_id ) ;
cdr_object_bridge_cmp_fn , bridge_id ) ;
if ( ! it_cdrs ) {
if ( ! it_cdrs ) {
/* No one in the bridge yet! */
/* No one in the bridge yet! */
@ -2350,10 +2337,9 @@ static struct ao2_container *create_candidates_for_bridge(struct ast_bridge_snap
add_candidate_for_bridge ( bridge - > uniqueid , candidates , cand_cdr_master , 1 ) ;
add_candidate_for_bridge ( bridge - > uniqueid , candidates , cand_cdr_master , 1 ) ;
}
}
ao2_iterator_destroy ( it_cdrs ) ;
ao2_iterator_destroy ( it_cdrs ) ;
/* For each CDR that has a record in the bridge, get their Party B and
/* For each CDR that has a record in the bridge, get their Party B and
* make them a candidate . */
* make them a candidate . */
it_cdrs = ao2_callback ( active_cdrs_by_ bridge, OBJ_MULTIPLE | OBJ_KEY ,
it_cdrs = ao2_callback ( active_cdrs_by_ channel, OBJ_MULTIPLE ,
cdr_object_bridge_cmp_fn , bridge_id ) ;
cdr_object_bridge_cmp_fn , bridge_id ) ;
if ( ! it_cdrs ) {
if ( ! it_cdrs ) {
/* Now it's just an error. */
/* Now it's just an error. */
@ -2412,7 +2398,6 @@ static int bridge_candidate_process(void *obj, void *arg, int flags)
| | ( cdr - > party_b . snapshot & & ! ( strcmp ( cdr - > party_b . snapshot - > name , bcand - > candidate . snapshot - > name ) ) ) ) {
| | ( cdr - > party_b . snapshot & & ! ( strcmp ( cdr - > party_b . snapshot - > name , bcand - > candidate . snapshot - > name ) ) ) ) {
return 0 ;
return 0 ;
}
}
party_a = cdr_object_pick_party_a ( & cdr - > party_a , & bcand - > candidate ) ;
party_a = cdr_object_pick_party_a ( & cdr - > party_a , & bcand - > candidate ) ;
/* We're party A - make a new CDR, append it to us, and set the candidate as
/* We're party A - make a new CDR, append it to us, and set the candidate as
* Party B */
* Party B */
@ -2454,7 +2439,6 @@ static int bridge_candidate_process(void *obj, void *arg, int flags)
} else {
} else {
bridge_candidate_add_to_cdr ( b_party , cdr - > bridge , & cdr - > party_a ) ;
bridge_candidate_add_to_cdr ( b_party , cdr - > bridge , & cdr - > party_a ) ;
}
}
ao2_link ( active_cdrs_by_bridge , b_party ) ;
ao2_ref ( b_party , - 1 ) ;
ao2_ref ( b_party , - 1 ) ;
}
}
@ -2476,7 +2460,6 @@ static void handle_bridge_pairings(struct cdr_object *cdr, struct ast_bridge_sna
if ( ! candidates ) {
if ( ! candidates ) {
return ;
return ;
}
}
ao2_callback ( candidates , OBJ_NODATA ,
ao2_callback ( candidates , OBJ_NODATA ,
bridge_candidate_process ,
bridge_candidate_process ,
cdr ) ;
cdr ) ;
@ -2577,8 +2560,6 @@ static void handle_standard_bridge_enter_message(struct cdr_object *cdr,
handled_cdr = cdr - > last ;
handled_cdr = cdr - > last ;
}
}
handle_bridge_pairings ( handled_cdr , bridge ) ;
handle_bridge_pairings ( handled_cdr , bridge ) ;
ao2_link ( active_cdrs_by_bridge , cdr ) ;
ao2_unlock ( cdr ) ;
ao2_unlock ( cdr ) ;
}
}
@ -2879,9 +2860,7 @@ static int cdr_object_select_all_by_channel_cb(void *obj, void *arg, int flags)
{
{
struct cdr_object * cdr = obj ;
struct cdr_object * cdr = obj ;
const char * name = arg ;
const char * name = arg ;
if ( ! ( flags & OBJ_KEY ) ) {
return 0 ;
}
if ( ! strcasecmp ( cdr - > party_a . snapshot - > name , name ) | |
if ( ! strcasecmp ( cdr - > party_a . snapshot - > name , name ) | |
( cdr - > party_b . snapshot & & ! strcasecmp ( cdr - > party_b . snapshot - > name , name ) ) ) {
( cdr - > party_b . snapshot & & ! strcasecmp ( cdr - > party_b . snapshot - > name , name ) ) ) {
return CMP_MATCH ;
return CMP_MATCH ;
@ -2910,7 +2889,7 @@ int ast_cdr_setvar(const char *channel_name, const char *name, const char *value
}
}
}
}
it_cdrs = ao2_callback ( active_cdrs_by_channel , OBJ_MULTIPLE | OBJ_KEY , cdr_object_select_all_by_channel_cb , arg ) ;
it_cdrs = ao2_callback ( active_cdrs_by_channel , OBJ_MULTIPLE , cdr_object_select_all_by_channel_cb , arg ) ;
if ( ! it_cdrs ) {
if ( ! it_cdrs ) {
ast_log ( AST_LOG_ERROR , " Unable to find CDR for channel %s \n " , channel_name ) ;
ast_log ( AST_LOG_ERROR , " Unable to find CDR for channel %s \n " , channel_name ) ;
return - 1 ;
return - 1 ;
@ -3240,7 +3219,11 @@ int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option)
if ( it_cdr - > fn_table = = & finalized_state_fn_table ) {
if ( it_cdr - > fn_table = = & finalized_state_fn_table ) {
continue ;
continue ;
}
}
/* Note: in general, set the flags on both the CDR record as well as the
* Party A . Sometimes all we have is the Party A to look at .
*/
ast_set_flag ( & it_cdr - > flags , option ) ;
ast_set_flag ( & it_cdr - > flags , option ) ;
ast_set_flag ( & it_cdr - > party_a , option ) ;
}
}
ao2_unlock ( cdr ) ;
ao2_unlock ( cdr ) ;
@ -3584,7 +3567,10 @@ static char *handle_cli_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
switch ( cmd ) {
switch ( cmd ) {
case CLI_INIT :
case CLI_INIT :
e - > command = " cdr set debug [on|off] " ;
e - > command = " cdr set debug [on|off] " ;
e - > usage = " Enable or disable extra debugging in the CDR Engine " ;
e - > usage = " Enable or disable extra debugging in the CDR Engine. Note \n "
" that this will dump debug information to the VERBOSE setting \n "
" and should only be used when debugging information from the \n "
" CDR engine is needed. \n " ;
return NULL ;
return NULL ;
case CLI_GENERATE :
case CLI_GENERATE :
return NULL ;
return NULL ;
@ -3605,6 +3591,181 @@ static char *handle_cli_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_a
return CLI_SUCCESS ;
return CLI_SUCCESS ;
}
}
/*! \brief Complete user input for 'cdr show' */
static char * cli_complete_show ( struct ast_cli_args * a )
{
char * result = NULL ;
int wordlen = strlen ( a - > word ) ;
int which = 0 ;
struct ao2_iterator it_cdrs ;
struct cdr_object * cdr ;
it_cdrs = ao2_iterator_init ( active_cdrs_by_channel , 0 ) ;
while ( ( cdr = ao2_iterator_next ( & it_cdrs ) ) ) {
if ( ! strncasecmp ( a - > word , cdr - > party_a . snapshot - > name , wordlen ) & &
( + + which > a - > n ) ) {
result = ast_strdup ( cdr - > party_a . snapshot - > name ) ;
if ( result ) {
ao2_ref ( cdr , - 1 ) ;
break ;
}
}
ao2_ref ( cdr , - 1 ) ;
}
ao2_iterator_destroy ( & it_cdrs ) ;
return result ;
}
static void cli_show_channels ( struct ast_cli_args * a )
{
struct ao2_iterator it_cdrs ;
struct cdr_object * cdr ;
char start_time_buffer [ 64 ] ;
char answer_time_buffer [ 64 ] = " \0 " ;
char end_time_buffer [ 64 ] ;
# define TITLE_STRING "%-25.25s %-25.25s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n"
# define FORMAT_STRING "%-25.25s %-25.25s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n"
ast_cli ( a - > fd , " \n " ) ;
ast_cli ( a - > fd , " Channels with Call Detail Record (CDR) Information \n " ) ;
ast_cli ( a - > fd , " -------------------------------------------------- \n " ) ;
ast_cli ( a - > fd , TITLE_STRING , " Channel " , " Dst. Channel " , " LastApp " , " Start " , " Answer " , " End " , " Billsec " , " Duration " ) ;
it_cdrs = ao2_iterator_init ( active_cdrs_by_channel , 0 ) ;
while ( ( cdr = ao2_iterator_next ( & it_cdrs ) ) ) {
struct cdr_object * it_cdr ;
struct timeval start_time = { 0 , } ;
struct timeval answer_time = { 0 , } ;
struct timeval end_time = { 0 , } ;
SCOPED_AO2LOCK ( lock , cdr ) ;
/* Calculate the start, end, answer, billsec, and duration over the
* life of all of the CDR entries
*/
for ( it_cdr = cdr ; it_cdr ; it_cdr = it_cdr - > next ) {
if ( snapshot_is_dialed ( it_cdr - > party_a . snapshot ) ) {
continue ;
}
if ( ast_tvzero ( start_time ) ) {
start_time = it_cdr - > start ;
}
if ( ! ast_tvzero ( it_cdr - > answer ) & & ast_tvzero ( answer_time ) ) {
answer_time = it_cdr - > answer ;
}
}
/* Only CDRs when this was dialed are available; 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 ;
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 ) ) ;
ast_cli ( a - > fd , FORMAT_STRING , it_cdr - > party_a . snapshot - > name ,
it_cdr - > party_b . snapshot ? it_cdr - > party_b . snapshot - > name : " <none> " ,
it_cdr - > appl ,
start_time_buffer ,
answer_time_buffer ,
end_time_buffer ,
( long ) ast_tvdiff_ms ( end_time , answer_time ) / 1000 ,
( long ) ast_tvdiff_ms ( end_time , start_time ) / 1000 ) ;
ao2_ref ( cdr , - 1 ) ;
}
ao2_iterator_destroy ( & it_cdrs ) ;
# undef FORMAT_STRING
# undef TITLE_STRING
}
static void cli_show_channel ( struct ast_cli_args * a )
{
struct cdr_object * it_cdr ;
char clid [ 64 ] ;
char start_time_buffer [ 64 ] ;
char answer_time_buffer [ 64 ] = " \0 " ;
char end_time_buffer [ 64 ] = " \0 " ;
const char * channel_name = a - > argv [ 3 ] ;
RAII_VAR ( struct cdr_object * , cdr , NULL , ao2_cleanup ) ;
# define TITLE_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n"
# define FORMAT_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n"
cdr = ao2_find ( active_cdrs_by_channel , channel_name , OBJ_KEY ) ;
if ( ! cdr ) {
ast_cli ( a - > fd , " Unknown channel: %s \n " , channel_name ) ;
return ;
}
ast_cli ( a - > fd , " \n " ) ;
ast_cli ( a - > fd , " Call Detail Record (CDR) Information for %s \n " , channel_name ) ;
ast_cli ( a - > fd , " -------------------------------------------------- \n " ) ;
ast_cli ( a - > fd , TITLE_STRING , " AccountCode " , " CallerID " , " Dst. Channel " , " LastApp " , " Data " , " Start " , " Answer " , " End " , " Billsec " , " Duration " ) ;
ao2_lock ( cdr ) ;
for ( it_cdr = cdr ; it_cdr ; it_cdr = it_cdr - > next ) {
struct timeval end ;
if ( snapshot_is_dialed ( it_cdr - > party_a . snapshot ) ) {
continue ;
}
ast_callerid_merge ( clid , sizeof ( clid ) , it_cdr - > party_a . snapshot - > caller_name , it_cdr - > party_a . snapshot - > caller_number , " " ) ;
if ( ast_tvzero ( it_cdr - > end ) ) {
end = ast_tvnow ( ) ;
} else {
end = it_cdr - > end ;
}
cdr_get_tv ( it_cdr - > start , " %T " , start_time_buffer , sizeof ( start_time_buffer ) ) ;
cdr_get_tv ( it_cdr - > answer , " %T " , answer_time_buffer , sizeof ( answer_time_buffer ) ) ;
cdr_get_tv ( end , " %T " , end_time_buffer , sizeof ( end_time_buffer ) ) ;
ast_cli ( a - > fd , FORMAT_STRING ,
it_cdr - > party_a . snapshot - > accountcode ,
clid ,
it_cdr - > party_b . snapshot ? it_cdr - > party_b . snapshot - > name : " <none> " ,
it_cdr - > appl ,
it_cdr - > data ,
start_time_buffer ,
answer_time_buffer ,
end_time_buffer ,
( long ) ast_tvdiff_ms ( end , it_cdr - > answer ) / 1000 ,
( long ) ast_tvdiff_ms ( end , it_cdr - > start ) / 1000 ) ;
}
ao2_unlock ( cdr ) ;
# undef FORMAT_STRING
# undef TITLE_STRING
}
static char * handle_cli_show ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
switch ( cmd ) {
case CLI_INIT :
e - > command = " cdr show active " ;
e - > usage =
" Usage: cdr show active [channel] \n "
" Displays a summary of all Call Detail Records when [channel] \n "
" is omitted; displays all of the Call Detail Records \n "
" currently in flight for a given [channel] when [channel] is \n "
" specified. \n \n "
" Note that this will not display Call Detail Records that \n "
" have already been dispatched to a backend storage, nor for \n "
" channels that are no longer active. \n " ;
return NULL ;
case CLI_GENERATE :
return cli_complete_show ( a ) ;
}
if ( a - > argc > 4 ) {
return CLI_SHOWUSAGE ;
} else if ( a - > argc < 4 ) {
cli_show_channels ( a ) ;
} else {
cli_show_channel ( a ) ;
}
return CLI_SUCCESS ;
}
static char * handle_cli_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
static char * handle_cli_status ( struct ast_cli_entry * e , int cmd , struct ast_cli_args * a )
{
{
struct cdr_beitem * beitem = NULL ;
struct cdr_beitem * beitem = NULL ;
@ -3623,8 +3784,9 @@ static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_
return NULL ;
return NULL ;
}
}
if ( a - > argc > 3 )
if ( a - > argc > 3 ) {
return CLI_SHOWUSAGE ;
return CLI_SHOWUSAGE ;
}
ast_cli ( a - > fd , " \n " ) ;
ast_cli ( a - > fd , " \n " ) ;
ast_cli ( a - > fd , " Call Detail Record (CDR) settings \n " ) ;
ast_cli ( a - > fd , " Call Detail Record (CDR) settings \n " ) ;
@ -3688,6 +3850,7 @@ static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_
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_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_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_debug = AST_CLI_DEFINE ( handle_cli_debug , " Enable debugging " ) ;
@ -3773,6 +3936,7 @@ static void cdr_engine_shutdown(void)
finalize_batch_mode ( ) ;
finalize_batch_mode ( ) ;
ast_cli_unregister ( & cli_status ) ;
ast_cli_unregister ( & cli_status ) ;
ast_cli_unregister ( & cli_debug ) ;
ast_cli_unregister ( & cli_debug ) ;
ast_cli_unregister ( & cli_show ) ;
ast_sched_context_destroy ( sched ) ;
ast_sched_context_destroy ( sched ) ;
sched = NULL ;
sched = NULL ;
ast_free ( batch ) ;
ast_free ( batch ) ;
@ -3785,7 +3949,6 @@ static void cdr_engine_shutdown(void)
ao2_global_obj_release ( module_configs ) ;
ao2_global_obj_release ( module_configs ) ;
ao2_ref ( active_cdrs_by_channel , - 1 ) ;
ao2_ref ( active_cdrs_by_channel , - 1 ) ;
ao2_ref ( active_cdrs_by_bridge , - 1 ) ;
}
}
static void cdr_enable_batch_mode ( struct ast_cdr_config * config )
static void cdr_enable_batch_mode ( struct ast_cdr_config * config )
@ -3809,6 +3972,31 @@ static void cdr_enable_batch_mode(struct ast_cdr_config *config)
config - > batch_settings . size , config - > batch_settings . time ) ;
config - > batch_settings . size , config - > batch_settings . time ) ;
}
}
/*!
* \ internal
* \ brief Print channel object key ( name ) .
* \ since 12.0 .0
*
* \ param v_obj A pointer to the object we want the key 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_container_print_fn ( void * v_obj , void * where , ao2_prnt_fn * prnt )
{
struct cdr_object * cdr = v_obj ;
struct cdr_object * it_cdr ;
if ( ! cdr ) {
return ;
}
for ( it_cdr = cdr ; it_cdr ; it_cdr = it_cdr - > next ) {
prnt ( where , " Party A: %s; Party B: %s; Bridge %s \n " , it_cdr - > party_a . snapshot - > name , it_cdr - > party_b . snapshot ? it_cdr - > party_b . snapshot - > name : " <unknown> " ,
it_cdr - > bridge ) ;
}
}
int ast_cdr_engine_init ( void )
int ast_cdr_engine_init ( void )
{
{
RAII_VAR ( struct module_config * , mod_cfg , NULL , ao2_cleanup ) ;
RAII_VAR ( struct module_config * , mod_cfg , NULL , ao2_cleanup ) ;
@ -3822,11 +4010,7 @@ int ast_cdr_engine_init(void)
if ( ! active_cdrs_by_channel ) {
if ( ! active_cdrs_by_channel ) {
return - 1 ;
return - 1 ;
}
}
ao2_container_register ( " cdrs_by_channel " , active_cdrs_by_channel , cdr_container_print_fn ) ;
active_cdrs_by_bridge = ao2_container_alloc ( 51 , cdr_object_bridge_hash_fn , cdr_object_bridge_cmp_fn ) ;
if ( ! active_cdrs_by_bridge ) {
return - 1 ;
}
cdr_topic = stasis_topic_create ( " cdr_engine " ) ;
cdr_topic = stasis_topic_create ( " cdr_engine " ) ;
if ( ! cdr_topic ) {
if ( ! cdr_topic ) {
@ -3864,6 +4048,7 @@ int ast_cdr_engine_init(void)
ast_cli_register ( & cli_status ) ;
ast_cli_register ( & cli_status ) ;
ast_cli_register ( & cli_debug ) ;
ast_cli_register ( & cli_debug ) ;
ast_cli_register ( & cli_show ) ;
ast_register_atexit ( cdr_engine_shutdown ) ;
ast_register_atexit ( cdr_engine_shutdown ) ;
mod_cfg = ao2_global_obj_ref ( module_configs ) ;
mod_cfg = ao2_global_obj_ref ( module_configs ) ;