68 #include "asterisk/stasis_channels.h"
69 #include "asterisk/stasis_bridges.h"
70 #include "asterisk/stasis_message_router.h"
227 #define DEFAULT_ENABLED "1"
228 #define DEFAULT_BATCHMODE "0"
229 #define DEFAULT_UNANSWERED "0"
230 #define DEFAULT_CONGESTION "0"
231 #define DEFAULT_END_BEFORE_H_EXTEN "1"
232 #define DEFAULT_INITIATED_SECONDS "0"
233 #define DEFAULT_CHANNEL_ENABLED "1"
234 #define DEFAULT_IGNORE_STATE_CHANGES "0"
235 #define DEFAULT_IGNORE_DIAL_CHANGES "0"
237 #define DEFAULT_BATCH_SIZE "100"
238 #define MAX_BATCH_SIZE 1000
239 #define DEFAULT_BATCH_TIME "300"
240 #define MAX_BATCH_TIME 86400
241 #define DEFAULT_BATCH_SCHEDULER_ONLY "0"
242 #define DEFAULT_BATCH_SAFE_SHUTDOWN "1"
244 #define cdr_set_debug_mode(mod_cfg) \
246 cdr_debug_enabled = ast_test_flag(&(mod_cfg)->general->settings, CDR_DEBUG); \
249 static int cdr_debug_enabled;
250 static int dial_changes_ignored;
252 #define CDR_DEBUG(fmt, ...) \
254 if (cdr_debug_enabled) { \
255 ast_verbose((fmt), ##__VA_ARGS__); \
259 static void cdr_detach(
struct ast_cdr *cdr);
260 static void cdr_submit_batch(
int shutdown);
276 .category =
"general",
294 static struct aco_type ignore_option = {
303 static void module_config_post_apply(
void);
308 .types =
ACO_TYPES(&general_option, &ignore_option),
312 .files = ACO_FILES(&module_file_conf),
313 .post_apply_config = module_config_post_apply,
318 static void module_config_post_apply(
void)
326 cdr_set_debug_mode(mod_cfg);
327 ao2_cleanup(mod_cfg);
352 cdr_config = ao2_alloc(
sizeof(*cdr_config), NULL);
395 static int cdr_sched = -1;
396 AST_MUTEX_DEFINE_STATIC(cdr_sched_lock);
397 static pthread_t cdr_thread = AST_PTHREADT_NULL;
404 static ast_cond_t cdr_pending_cond;
525 const char *dial_status);
598 static void single_state_init_function(
struct cdr_object *cdr);
617 .init_function = single_state_init_function,
618 .process_party_a = base_process_party_a,
619 .process_party_b = single_state_process_party_b,
620 .process_dial_begin = single_state_process_dial_begin,
621 .process_dial_end = base_process_dial_end,
622 .process_bridge_enter = single_state_process_bridge_enter,
623 .process_parking_bridge_enter = single_state_process_parking_bridge_enter,
624 .process_bridge_leave = base_process_bridge_leave,
625 .process_parked_channel = base_process_parked_channel,
648 .process_party_a = base_process_party_a,
649 .process_party_b = dial_state_process_party_b,
650 .process_dial_begin = dial_state_process_dial_begin,
651 .process_dial_end = dial_state_process_dial_end,
652 .process_bridge_enter = dial_state_process_bridge_enter,
653 .process_bridge_leave = base_process_bridge_leave,
680 .
name =
"DialedPending",
681 .process_party_a = dialed_pending_state_process_party_a,
682 .process_dial_begin = dialed_pending_state_process_dial_begin,
683 .process_bridge_enter = dialed_pending_state_process_bridge_enter,
684 .process_parking_bridge_enter = dialed_pending_state_process_parking_bridge_enter,
685 .process_bridge_leave = base_process_bridge_leave,
686 .process_parked_channel = base_process_parked_channel,
703 .process_party_a = base_process_party_a,
704 .process_party_b = bridge_state_process_party_b,
705 .process_bridge_leave = bridge_state_process_bridge_leave,
706 .process_parked_channel = base_process_parked_channel,
722 .process_party_a = base_process_party_a,
723 .process_bridge_leave = parked_state_process_bridge_leave,
724 .process_parked_channel = base_process_parked_channel,
727 static void finalized_state_init_function(
struct cdr_object *cdr);
738 .init_function = finalized_state_init_function,
739 .process_party_a = finalized_state_process_party_a,
740 .process_bridge_enter = base_process_bridge_enter,
797 var = ast_var_name(variables);
798 if (ast_strlen_zero(var)) {
801 val = ast_var_value(variables);
802 if (ast_strlen_zero(val)) {
805 newvariable = ast_var_assign(var, val);
824 ast_var_delete(vardata);
849 CDR_DEBUG(
"%p - Transitioning CDR for %s from state %s to %s\n",
873 static int cdr_master_hash_fn(
const void *obj,
const int flags)
897 static int cdr_master_cmp_fn(
void *obj,
void *arg,
int flags)
901 const char *right_key = arg;
909 cmp = strcmp(left->
uniqueid, right_key);
916 cmp = strncmp(left->
uniqueid, right_key, strlen(right_key));
931 static int cdr_all_hash_fn(
const void *obj,
const int flags)
936 switch (flags & OBJ_SEARCH_MASK) {
955 static int cdr_all_cmp_fn(
void *obj,
void *arg,
int flags)
959 const char *right_key = arg;
962 switch (flags & OBJ_SEARCH_MASK) {
974 cmp = strncasecmp(left->
party_b_name, right_key, strlen(right_key));
990 static void cdr_all_relink(
struct cdr_object *cdr)
992 ao2_lock(active_cdrs_all);
1003 ao2_unlock(active_cdrs_all);
1011 static void cdr_all_unlink(
struct cdr_object *cdr)
1020 ao2_lock(active_cdrs_all);
1021 for (cur = cdr; cur; cur =
next) {
1031 ao2_unlock(active_cdrs_all);
1046 ast_var_delete(it_var);
1049 ast_var_delete(it_var);
1083 ast_assert(chan != NULL);
1102 ao2_t_ref(cdr->
party_a.
snapshot, +1,
"bump snapshot during CDR creation");
1122 cdr_last = cdr->
last;
1148 for (it_cdr = cdr; it_cdr->
next; it_cdr = it_cdr->
next) {
1149 it_cdr->
last = new_cdr;
1151 it_cdr->
last = new_cdr;
1152 it_cdr->
next = new_cdr;
1166 static int is_cdr_flag_set(
unsigned int cdr_flag)
1172 flag_set = mod_cfg && ast_test_flag(&mod_cfg->
general->
settings, cdr_flag);
1173 ao2_cleanup(mod_cfg);
1201 && strncasecmp(new_snapshot->
dialplan->
appl,
"appdial", 7)
1283 if (ms % 1000 >= 500
1285 ms = (ms / 1000) + 1;
1301 static void set_variable(
struct varshead *headp,
const char *name,
const char *value)
1306 if (!strcasecmp(ast_var_name(newvariable), name)) {
1308 ast_var_delete(newvariable);
1314 if (value && (newvariable = ast_var_assign(name, value))) {
1328 struct ast_cdr *pub_cdr = NULL, *cdr_prev = NULL;
1334 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
1339 ast_debug(1,
"CDR for %s is dialed and has no Party B; discarding\n",
1354 ast_assert(party_a != NULL);
1379 cdr_copy->start = it_cdr->
start;
1380 cdr_copy->answer = it_cdr->
answer;
1381 cdr_copy->end = it_cdr->
end;
1386 ast_copy_flags(cdr_copy, &it_cdr->
flags, AST_FLAGS_ALL);
1397 if (!strcasecmp(ast_var_name(it_var), ast_var_name(it_copy_var))) {
1402 if (!found && (newvariable = ast_var_assign(ast_var_name(it_var), ast_var_value(it_var)))) {
1411 cdr_prev->next = cdr_copy;
1412 cdr_prev = cdr_copy;
1429 CDR_DEBUG(
"%p - Dispatching CDR for Party A %s, Party B %s\n", cdr,
1433 cdr_detach(pub_cdr);
1444 switch (hangupcause) {
1445 case AST_CAUSE_BUSY:
1448 case AST_CAUSE_CONGESTION:
1455 case AST_CAUSE_NO_ROUTE_DESTINATION:
1456 case AST_CAUSE_UNREGISTERED:
1459 case AST_CAUSE_NORMAL_CLEARING:
1460 case AST_CAUSE_NO_ANSWER:
1499 ast_debug(1,
"Finalized CDR for %s - start %ld.%06ld answer %ld.%06ld end %ld.%06ld dur %.3f bill %.3f dispo %s\n",
1501 (
long)cdr->
start.tv_sec,
1502 (
long)cdr->
start.tv_usec,
1503 (
long)cdr->
answer.tv_sec,
1504 (
long)cdr->
answer.tv_usec,
1505 (
long)cdr->
end.tv_sec,
1506 (
long)cdr->
end.tv_usec,
1524 && cdr->
fn_table != &finalized_state_fn_table) {
1538 CDR_DEBUG(
"%p - Set answered time to %ld.%06ld\n", cdr,
1539 (
long)cdr->
answer.tv_sec,
1540 (
long)cdr->
answer.tv_usec);
1574 ao2_t_replace(old_snapshot->
snapshot, new_snapshot,
"Swap CDR shapshot");
1613 && (strncasecmp(snapshot->
dialplan->
appl,
"appdial", 7) || ast_strlen_zero(cdr->
appl))) {
1658 char park_info[128];
1678 static void single_state_init_function(
struct cdr_object *cdr)
1695 base_process_party_a(cdr, caller);
1696 CDR_DEBUG(
"%p - Updated Party A %s snapshot\n", cdr,
1699 cdr_all_relink(cdr);
1700 CDR_DEBUG(
"%p - Updated Party B %s snapshot\n", cdr,
1710 base_process_party_a(cdr, peer);
1711 CDR_DEBUG(
"%p - Updated Party A %s snapshot\n", cdr,
1744 CDR_DEBUG(
"%p - Party A %s has new Party B %s\n",
1747 cdr_all_relink(cdr);
1765 CDR_DEBUG(
"%p - Party A %s has new Party B %s\n",
1768 cdr_all_relink(cdr);
1790 !success && (channel_id = ao2_iterator_next(&it_cdrs));
1795 cand_cdr_master = ao2_find(active_cdrs_master, channel_id,
OBJ_SEARCH_KEY);
1796 if (!cand_cdr_master) {
1800 ao2_lock(cand_cdr_master);
1801 for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->
next) {
1804 if (cand_cdr->
fn_table != &bridge_state_fn_table ||
1816 ao2_unlock(cand_cdr_master);
1817 ao2_cleanup(cand_cdr_master);
1843 ast_assert(snapshot != NULL);
1869 if (!strcmp(dial_status,
"ANSWER")) {
1870 return AST_CDR_ANSWERED;
1871 }
else if (!strcmp(dial_status,
"BUSY")) {
1872 return AST_CDR_BUSY;
1873 }
else if (!strcmp(dial_status,
"CANCEL") || !strcmp(dial_status,
"NOANSWER")) {
1874 return AST_CDR_NOANSWER;
1875 }
else if (!strcmp(dial_status,
"CONGESTION")) {
1877 return AST_CDR_FAILED;
1879 return AST_CDR_CONGESTION;
1881 }
else if (!strcmp(dial_status,
"FAILED")) {
1882 return AST_CDR_FAILED;
1884 return AST_CDR_FAILED;
1908 cdr->
disposition = dial_status_to_disposition(dial_status);
1938 !success && (channel_id = ao2_iterator_next(&it_cdrs));
1943 cand_cdr_master = ao2_find(active_cdrs_master, channel_id,
OBJ_SEARCH_KEY);
1944 if (!cand_cdr_master) {
1948 ao2_lock(cand_cdr_master);
1949 for (cand_cdr = cand_cdr_master; cand_cdr; cand_cdr = cand_cdr->
next) {
1952 if (cand_cdr->
fn_table != &bridge_state_fn_table
1972 ao2_unlock(cand_cdr_master);
1973 ao2_cleanup(cand_cdr_master);
2011 base_process_party_a(cdr, snapshot);
2083 static void finalized_state_init_function(
struct cdr_object *cdr)
2119 ret |= filter_channel_snapshot(old_snapshot);
2122 ret |= filter_channel_snapshot(new_snapshot);
2128 static int dial_status_end(
const char *dialstatus)
2130 return (strcmp(dialstatus,
"RINGING") &&
2131 strcmp(dialstatus,
"PROCEEDING") &&
2132 strcmp(dialstatus,
"PROGRESS"));
2151 const char *dial_status = NULL;
2156 if (!peer && !caller) {
2160 if (peer && filter_channel_snapshot(peer)) {
2164 if (caller && filter_channel_snapshot(caller)) {
2169 if (dial_status_blob) {
2173 CDR_DEBUG(
"Dial %s message for %s, %s: %u.%08u\n",
2174 ast_strlen_zero(dial_status) ?
"Begin" :
"End",
2175 caller ? caller->
base->
name :
"(none)",
2176 peer ? peer->
base->
name :
"(none)",
2187 ast_log(AST_LOG_WARNING,
"No CDR for channel %s\n", caller ? caller->
base->
name : peer->
base->
name);
2193 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2195 if (ast_strlen_zero(dial_status)) {
2199 if (dial_changes_ignored) {
2200 CDR_DEBUG(
"%p - Ignoring Dial Begin message\n", it_cdr);
2203 CDR_DEBUG(
"%p - Processing Dial Begin message for channel %s, peer %s\n",
2205 caller ? caller->
base->
name :
"(none)",
2206 peer ? peer->
base->
name :
"(none)");
2210 }
else if (dial_status_end(dial_status)) {
2214 if (dial_changes_ignored) {
2216 it_cdr->
disposition = dial_status_to_disposition(dial_status);
2217 CDR_DEBUG(
"%p - Setting disposition and that's it (%s)\n", it_cdr, dial_status);
2220 CDR_DEBUG(
"%p - Processing Dial End message for channel %s, peer %s\n",
2222 caller ? caller->
base->
name :
"(none)",
2223 peer ? peer->
base->
name :
"(none)");
2232 if (!dial_changes_ignored) {
2234 if (res && ast_strlen_zero(dial_status)) {
2248 static int cdr_object_finalize_party_b(
void *obj,
void *arg,
void *
data,
int flags)
2272 static int cdr_object_update_party_b(
void *obj,
void *arg,
void *data,
int flags)
2288 "CDR for Party A %s(%s) has inconsistent Party B %s name. Message can be ignored but this shouldn't happen.\n",
2354 ast_debug(3,
"Disable CDR by default\n");
2357 ao2_cleanup(mod_cfg);
2371 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2393 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2400 cdr_all_unlink(cdr);
2421 const struct timeval *lastevent;
2430 if (cdr->
fn_table == &bridge_state_fn_table
2443 cdr->
lastevent = *leave_data->lastevent;
2455 if (!strcmp(bridge->
technology,
"holding_bridge") && strcmp(bridge->
subclass,
"parking")) {
2480 int left_bridge = 0;
2486 if (filter_channel_snapshot(channel)) {
2490 CDR_DEBUG(
"Bridge Leave message for %s: %u.%08u\n",
2492 (
unsigned int)leave_data.lastevent->tv_sec,
2493 (
unsigned int)leave_data.lastevent->tv_usec);
2497 ast_log(AST_LOG_WARNING,
"No CDR for channel %s\n", channel->
base->
name);
2504 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2505 it_cdr->
lastevent = *leave_data.lastevent;
2509 CDR_DEBUG(
"%p - Processing Bridge Leave for %s\n",
2520 && strcmp(bridge->
subclass,
"parking")) {
2535 static void bridge_candidate_add_to_cdr(
struct cdr_object *cdr,
2545 cdr_all_relink(new_cdr);
2549 CDR_DEBUG(
"%p - Party A %s has new Party B %s\n",
2571 ao2_lock(base_cand_cdr);
2573 for (cand_cdr = base_cand_cdr; cand_cdr; cand_cdr = cand_cdr->
next) {
2590 bridge_candidate_add_to_cdr(cdr, &cand_cdr->
party_a);
2598 bridge_candidate_add_to_cdr(cand_cdr, &cdr->
party_a);
2600 CDR_DEBUG(
"%p - Party A %s has new Party B %s\n",
2604 cdr_all_relink(cand_cdr);
2609 memset(&cand_cdr->
end, 0,
sizeof(cand_cdr->
end));
2615 ao2_unlock(base_cand_cdr);
2630 while ((channel_id = ao2_iterator_next(&it_channels))) {
2633 cand_cdr = ao2_find(active_cdrs_master, channel_id,
OBJ_SEARCH_KEY);
2653 const struct timeval *event_time)
2661 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2668 CDR_DEBUG(
"%p - Updating Party A %s snapshot\n", it_cdr,
2695 const struct timeval *event_time)
2705 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2709 CDR_DEBUG(
"%p - Updating Party A %s snapshot\n", it_cdr,
2716 CDR_DEBUG(
"%p - Processing bridge enter for %s\n", it_cdr,
2724 handled_cdr = it_cdr;
2735 handled_cdr = it_cdr;
2784 if (filter_channel_snapshot(channel)) {
2788 CDR_DEBUG(
"Bridge Enter message for channel %s: %u.%08u\n",
2795 ast_log(AST_LOG_WARNING,
"No CDR for channel %s\n", channel->
base->
name);
2800 if (!strcmp(bridge->
subclass,
"parking")) {
2833 if (filter_channel_snapshot(channel)) {
2837 CDR_DEBUG(
"Parked Call message for channel %s: %u.%08u\n",
2844 ast_log(AST_LOG_WARNING,
"No CDR for channel %s\n", channel->
base->
name);
2851 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
2898 ao2_cleanup(mod_cfg);
2917 cdr_set_debug_mode(mod_cfg);
2920 ao2_cleanup(mod_cfg);
2934 AST_RWLIST_TRAVERSE(&
be_list, i, list) {
2935 if (!strcasecmp(name, i->name)) {
2936 ast_debug(3,
"Suspending CDR backend %s\n", i->name);
2952 AST_RWLIST_TRAVERSE(&
be_list, i, list) {
2953 if (!strcasecmp(name, i->name)) {
2954 ast_debug(3,
"Unsuspending CDR backend %s\n", i->name);
2964 static int cdr_generic_register(
struct be_list *generic_list,
const char *name,
const char *desc,
ast_cdrbe be)
2974 ast_log(LOG_WARNING,
"CDR engine '%s' lacks backend\n", name);
2989 AST_RWLIST_TRAVERSE(generic_list, cur, list) {
2990 if (!strcasecmp(name, cur->name)) {
2991 ast_log(LOG_WARNING,
"Already have a CDR backend called '%s'\n", name);
2999 AST_RWLIST_INSERT_HEAD(generic_list, i, list);
3007 return cdr_generic_register(&
be_list, name, desc, be);
3012 return cdr_generic_register((
struct be_list *)&
mo_list, name, desc, be);
3015 static int ast_cdr_generic_unregister(
struct be_list *generic_list,
const char *name)
3021 AST_RWLIST_TRAVERSE(generic_list, match, list) {
3022 if (!strcasecmp(name, match->name)) {
3034 if (!match->suspended && active_count != 0) {
3036 ast_log(AST_LOG_WARNING,
"Unable to unregister CDR backend %s; %d CDRs are still active\n",
3037 name, active_count);
3041 AST_RWLIST_REMOVE(generic_list, match, list);
3044 ast_verb(5,
"Unregistered '%s' CDR backend\n", name);
3052 return ast_cdr_generic_unregister(&
be_list, name);
3057 return ast_cdr_generic_unregister((
struct be_list *)&
mo_list, name);
3075 newcdr->next = NULL;
3080 static const char *cdr_format_var_internal(
struct ast_cdr *cdr,
const char *name)
3084 if (ast_strlen_zero(name)) {
3089 if (!strcasecmp(name, ast_var_name(variables))) {
3090 return ast_var_value(variables);
3097 static void cdr_get_tv(
struct timeval when,
const char *fmt,
char *buf,
int bufsize)
3100 snprintf(buf, bufsize,
"%ld.%06ld", (
long)when.tv_sec, (
long)when.tv_usec);
3114 const char *fmt =
"%Y-%m-%d %T";
3123 if (!strcasecmp(name,
"clid")) {
3125 }
else if (!strcasecmp(name,
"src")) {
3127 }
else if (!strcasecmp(name,
"dst")) {
3129 }
else if (!strcasecmp(name,
"dcontext")) {
3131 }
else if (!strcasecmp(name,
"channel")) {
3133 }
else if (!strcasecmp(name,
"dstchannel")) {
3135 }
else if (!strcasecmp(name,
"lastapp")) {
3137 }
else if (!strcasecmp(name,
"lastdata")) {
3139 }
else if (!strcasecmp(name,
"start")) {
3140 cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
3141 }
else if (!strcasecmp(name,
"answer")) {
3142 cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
3143 }
else if (!strcasecmp(name,
"end")) {
3144 cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
3145 }
else if (!strcasecmp(name,
"duration")) {
3147 }
else if (!strcasecmp(name,
"billsec")) {
3149 }
else if (!strcasecmp(name,
"disposition")) {
3151 snprintf(workspace, workspacelen,
"%ld", cdr->
disposition);
3155 }
else if (!strcasecmp(name,
"amaflags")) {
3157 snprintf(workspace, workspacelen,
"%ld", cdr->
amaflags);
3161 }
else if (!strcasecmp(name,
"accountcode")) {
3163 }
else if (!strcasecmp(name,
"peeraccount")) {
3165 }
else if (!strcasecmp(name,
"uniqueid")) {
3167 }
else if (!strcasecmp(name,
"linkedid")) {
3169 }
else if (!strcasecmp(name,
"userfield")) {
3171 }
else if (!strcasecmp(name,
"sequence")) {
3172 snprintf(workspace, workspacelen,
"%d", cdr->
sequence);
3173 }
else if ((varbuf = cdr_format_var_internal(cdr, name))) {
3176 workspace[0] =
'\0';
3179 if (!ast_strlen_zero(workspace)) {
3188 static int cdr_object_select_all_by_name_cb(
void *obj,
void *arg,
int flags)
3191 const char *name = arg;
3204 static int cdr_object_get_by_name_cb(
void *obj,
void *arg,
int flags)
3207 const char *name = arg;
3216 static const char *
const cdr_readonly_vars[] = {
3248 for (x = 0; cdr_readonly_vars[x]; x++) {
3249 if (!strcasecmp(name, cdr_readonly_vars[x])) {
3250 ast_log(LOG_ERROR,
"Attempt to set the '%s' read-only variable!\n", name);
3257 ast_log(AST_LOG_ERROR,
"Unable to find CDR for channel %s\n", channel_name);
3261 for (; (cdr = ao2_iterator_next(it_cdrs)); ao2_unlock(cdr), ao2_cleanup(cdr)) {
3263 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
3266 if (it_cdr->
fn_table == &finalized_state_fn_table && it_cdr->
next != NULL) {
3276 set_variable(headp, name, value);
3293 if (!strcasecmp(name, ast_var_name(variable))) {
3310 if (!strcasecmp(name,
"clid")) {
3312 }
else if (!strcasecmp(name,
"src")) {
3314 }
else if (!strcasecmp(name,
"dst")) {
3316 }
else if (!strcasecmp(name,
"dcontext")) {
3318 }
else if (!strcasecmp(name,
"channel")) {
3320 }
else if (!strcasecmp(name,
"dstchannel")) {
3326 }
else if (!strcasecmp(name,
"lastapp")) {
3328 }
else if (!strcasecmp(name,
"lastdata")) {
3330 }
else if (!strcasecmp(name,
"start")) {
3331 cdr_get_tv(cdr_obj->
start, NULL, value, length);
3332 }
else if (!strcasecmp(name,
"answer")) {
3333 cdr_get_tv(cdr_obj->
answer, NULL, value, length);
3334 }
else if (!strcasecmp(name,
"end")) {
3335 cdr_get_tv(cdr_obj->
end, NULL, value, length);
3336 }
else if (!strcasecmp(name,
"duration")) {
3338 }
else if (!strcasecmp(name,
"billsec")) {
3340 }
else if (!strcasecmp(name,
"disposition")) {
3341 snprintf(value, length,
"%u", cdr_obj->
disposition);
3342 }
else if (!strcasecmp(name,
"amaflags")) {
3343 snprintf(value, length,
"%d", party_a->
amaflags);
3344 }
else if (!strcasecmp(name,
"accountcode")) {
3346 }
else if (!strcasecmp(name,
"peeraccount")) {
3352 }
else if (!strcasecmp(name,
"uniqueid")) {
3354 }
else if (!strcasecmp(name,
"linkedid")) {
3356 }
else if (!strcasecmp(name,
"userfield")) {
3358 }
else if (!strcasecmp(name,
"sequence")) {
3359 snprintf(value, length,
"%u", cdr_obj->
sequence);
3374 static struct cdr_object *cdr_object_get_by_name(
const char *name)
3378 if (ast_strlen_zero(name)) {
3383 return ao2_callback(active_cdrs_master, 0, cdr_object_get_by_name_cb, param);
3386 int ast_cdr_getvar(
const char *channel_name,
const char *name,
char *value,
size_t length)
3391 if (ast_strlen_zero(name)) {
3395 cdr = cdr_object_get_by_name(channel_name);
3397 ast_log(AST_LOG_ERROR,
"Unable to find CDR for channel %s\n", channel_name);
3403 cdr_obj = cdr->
last;
3421 char workspace[256];
3422 int total = 0, x = 0, i;
3424 cdr = cdr_object_get_by_name(channel_name);
3427 ast_log(AST_LOG_ERROR,
"Unable to find CDR for channel %s\n", channel_name);
3435 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
3441 if (!(var = ast_var_name(variable))) {
3445 if (
ast_str_append(buf, 0,
"level %d: %s%c%s%c", x, var, delim,
S_OR(ast_var_value(variable),
""), sep) < 0) {
3446 ast_log(LOG_ERROR,
"Data Buffer Size Exceeded!\n");
3453 for (i = 0; cdr_readonly_vars[i]; i++) {
3460 if (!ast_strlen_zero(workspace)
3461 &&
ast_str_append(buf, 0,
"level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, workspace, sep) < 0) {
3462 ast_log(LOG_ERROR,
"Data Buffer Size Exceeded!\n");
3476 struct ast_cdr *next = cdr->next;
3494 switch (disposition) {
3497 case AST_CDR_NOANSWER:
3499 case AST_CDR_FAILED:
3503 case AST_CDR_ANSWERED:
3505 case AST_CDR_CONGESTION:
3506 return "CONGESTION";
3512 const char *channel_name;
3513 const char *userfield;
3521 if ((cdr->
fn_table != &finalized_state_fn_table || !cdr->
next)
3543 .channel_name = channel_name,
3544 .userfield = userfield,
3549 cdr = cdr_object_get_by_name(channel_name);
3552 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
3553 if (it_cdr->
fn_table == &finalized_state_fn_table && it_cdr->
next != NULL) {
3570 static void post_cdr(
struct ast_cdr *cdr)
3580 for (; cdr ; cdr = cdr->next) {
3584 (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->
dstchannel))) {
3585 ast_debug(1,
"Skipping CDR for %s since we weren't answered\n", cdr->channel);
3591 AST_RWLIST_TRAVERSE(&
mo_list, i, list) {
3600 AST_RWLIST_TRAVERSE(&
be_list, i, list) {
3601 if (!i->suspended) {
3607 ao2_cleanup(mod_cfg);
3615 cdr = cdr_object_get_by_name(channel_name);
3621 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
3622 if (it_cdr->
fn_table == &finalized_state_fn_table) {
3628 ast_set_flag(&it_cdr->
flags, option);
3629 ast_set_flag(&it_cdr->
party_a, option);
3642 cdr = cdr_object_get_by_name(channel_name);
3648 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
3649 if (it_cdr->
fn_table == &finalized_state_fn_table) {
3652 ast_clear_flag(&it_cdr->
flags, option);
3666 cdr = cdr_object_get_by_name(channel_name);
3672 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
3674 if (!keep_variables) {
3676 ast_var_delete(vardata);
3680 ast_var_delete(vardata);
3686 memset(&it_cdr->
start, 0,
sizeof(it_cdr->
start));
3687 memset(&it_cdr->
end, 0,
sizeof(it_cdr->
end));
3701 RAII_VAR(
struct cdr_object *, cdr, cdr_object_get_by_name(channel_name), ao2_cleanup);
3714 cdr_obj = cdr->last;
3715 if (cdr_obj->
fn_table == &finalized_state_fn_table) {
3725 ast_debug(1,
"Forking CDR for channel %s\n", cdr->party_a.snapshot->base->name);
3747 cdr_all_relink(new_cdr);
3775 for (it_cdr = cdr; it_cdr != new_cdr; it_cdr = it_cdr->
next) {
3776 if (it_cdr->
fn_table == &finalized_state_fn_table) {
3811 static void *do_batch_backend_process(
void *data)
3818 post_cdr(batchitem->cdr);
3820 processeditem = batchitem;
3821 batchitem = batchitem->next;
3822 ast_free(processeditem);
3828 static void cdr_submit_batch(
int do_shutdown)
3832 pthread_t batch_post_thread = AST_PTHREADT_NULL;
3835 if (!batch || !batch->head) {
3841 oldbatchitems = batch->head;
3852 ast_debug(1,
"CDR single-threaded batch processing begins now\n");
3853 do_batch_backend_process(oldbatchitems);
3855 if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
3856 ast_log(LOG_WARNING,
"CDR processing thread could not detach, now trying in this thread\n");
3857 do_batch_backend_process(oldbatchitems);
3859 ast_debug(1,
"CDR multi-threaded batch processing begins now\n");
3863 ao2_cleanup(mod_cfg);
3866 static int submit_scheduled_batch(
const void *data)
3871 cdr_submit_batch(0);
3879 nextms = mod_cfg->
general->batch_settings.
time * 1000;
3881 ao2_cleanup(mod_cfg);
3890 ast_mutex_lock(&cdr_sched_lock);
3895 ast_mutex_unlock(&cdr_sched_lock);
3899 ast_cond_signal(&cdr_pending_cond);
3903 static void cdr_detach(
struct ast_cdr *cdr)
3908 int submit_batch = 0;
3929 ast_debug(1,
"CDR detaching from this thread\n");
3932 if (!(newtail =
ast_calloc(1,
sizeof(*newtail)))) {
3944 batch->head = newtail;
3947 batch->tail->next = newtail;
3950 batch->tail = newtail;
3951 curr = batch->size++;
3954 if (curr >= (mod_cfg->
general->batch_settings.
size - 1)) {
3965 static void *do_cdr(
void *data)
3967 struct timespec timeout;
3978 timeout.tv_sec = now.tv_sec;
3979 timeout.tv_nsec = now.tv_usec * 1000;
3985 ast_debug(2,
"Processed %d CDR batches from the run queue\n", numevents);
3997 e->
command =
"cdr set debug [on|off]";
3998 e->
usage =
"Enable or disable extra debugging in the CDR Engine. Note\n"
3999 "that this will dump debug information to the VERBOSE setting\n"
4000 "and should only be used when debugging information from the\n"
4001 "CDR engine is needed.\n";
4008 return CLI_SHOWUSAGE;
4013 ast_cli(a->fd,
"Could not set CDR debugging mode\n");
4016 if (!strcasecmp(a->argv[3],
"on")
4019 ast_cli(a->fd,
"CDR debugging enabled\n");
4020 }
else if (!strcasecmp(a->argv[3],
"off")
4023 ast_cli(a->fd,
"CDR debugging disabled\n");
4025 cdr_set_debug_mode(mod_cfg);
4026 ao2_cleanup(mod_cfg);
4034 int wordlen = strlen(a->word);
4039 while ((cdr = ao2_iterator_next(&it_cdrs))) {
4057 char start_time_buffer[64];
4058 char answer_time_buffer[64];
4059 char end_time_buffer[64];
4061 #define TITLE_STRING "%-25.25s %-25.25s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n"
4062 #define FORMAT_STRING "%-25.25s %-25.25s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n"
4064 ast_cli(a->fd,
"\n");
4065 ast_cli(a->fd,
"Channels with Call Detail Record (CDR) Information\n");
4066 ast_cli(a->fd,
"--------------------------------------------------\n");
4067 ast_cli(a->fd, TITLE_STRING,
"Channel",
"Dst. Channel",
"LastApp",
"Start",
"Answer",
"End",
"Billsec",
"Duration");
4070 for (; (cdr = ao2_iterator_next(&it_cdrs)); ao2_cleanup(cdr)) {
4072 struct timeval start_time = { 0, };
4073 struct timeval answer_time = { 0, };
4074 struct timeval end_time = { 0, };
4081 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
4086 start_time = it_cdr->
start;
4089 answer_time = it_cdr->
answer;
4100 cdr_get_tv(start_time,
"%T", start_time_buffer,
sizeof(start_time_buffer));
4101 cdr_get_tv(answer_time,
"%T", answer_time_buffer,
sizeof(answer_time_buffer));
4102 cdr_get_tv(end_time,
"%T", end_time_buffer,
sizeof(end_time_buffer));
4113 #undef FORMAT_STRING
4121 char start_time_buffer[64];
4122 char answer_time_buffer[64];
4123 char end_time_buffer[64];
4124 const char *channel_name = a->argv[3];
4127 #define TITLE_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n"
4128 #define FORMAT_STRING "%-10.10s %-20.20s %-25.25s %-15.15s %-15.15s %-8.8s %-8.8s %-8.8s %-8.8ld %-8.8ld\n"
4130 cdr = cdr_object_get_by_name(channel_name);
4132 ast_cli(a->fd,
"Unknown channel: %s\n", channel_name);
4136 ast_cli(a->fd,
"\n");
4137 ast_cli(a->fd,
"Call Detail Record (CDR) Information for %s\n", channel_name);
4138 ast_cli(a->fd,
"--------------------------------------------------\n");
4139 ast_cli(a->fd, TITLE_STRING,
"AccountCode",
"CallerID",
"Dst. Channel",
"LastApp",
"Data",
"Start",
"Answer",
"End",
"Billsec",
"Duration");
4142 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
4154 cdr_get_tv(it_cdr->
start,
"%T", start_time_buffer,
sizeof(start_time_buffer));
4155 cdr_get_tv(it_cdr->
answer,
"%T", answer_time_buffer,
sizeof(answer_time_buffer));
4156 cdr_get_tv(end,
"%T", end_time_buffer,
sizeof(end_time_buffer));
4157 ast_cli(a->fd, FORMAT_STRING,
4173 #undef FORMAT_STRING
4181 e->
command =
"cdr show active";
4183 "Usage: cdr show active [channel]\n"
4184 " Displays a summary of all Call Detail Records when [channel]\n"
4185 " is omitted; displays all of the Call Detail Records\n"
4186 " currently in flight for a given [channel] when [channel] is\n"
4188 " Note that this will not display Call Detail Records that\n"
4189 " have already been dispatched to a backend storage, nor for\n"
4190 " channels that are no longer active.\n";
4197 return CLI_SHOWUSAGE;
4198 }
else if (a->argc < 4) {
4199 cli_show_channels(a);
4201 cli_show_channel(a);
4212 long nextbatchtime = 0;
4216 e->
command =
"cdr show status";
4218 "Usage: cdr show status\n"
4219 " Displays the Call Detail Record engine system status.\n";
4226 return CLI_SHOWUSAGE;
4234 ast_cli(a->fd,
"\n");
4235 ast_cli(a->fd,
"Call Detail Record (CDR) settings\n");
4236 ast_cli(a->fd,
"----------------------------------\n");
4246 ast_cli(a->fd,
"* Batch Mode Settings\n");
4247 ast_cli(a->fd,
" -------------------\n");
4254 ast_cli(a->fd,
" Current batch size: %d record%s\n", cnt,
ESS(cnt));
4255 ast_cli(a->fd,
" Maximum batch size: %u record%s\n", mod_cfg->
general->batch_settings.
size,
ESS(mod_cfg->
general->batch_settings.
size));
4256 ast_cli(a->fd,
" Maximum batch time: %u second%s\n", mod_cfg->
general->batch_settings.
time,
ESS(mod_cfg->
general->batch_settings.
time));
4257 ast_cli(a->fd,
" Next batch processing time: %ld second%s\n\n", nextbatchtime,
ESS(nextbatchtime));
4259 ast_cli(a->fd,
"* Registered Backends\n");
4260 ast_cli(a->fd,
" -------------------\n");
4262 if (AST_RWLIST_EMPTY(&
be_list)) {
4263 ast_cli(a->fd,
" (none)\n");
4265 AST_RWLIST_TRAVERSE(&
be_list, beitem, list) {
4266 ast_cli(a->fd,
" %s%s\n", beitem->name, beitem->suspended ?
" (suspended) " :
"");
4270 ast_cli(a->fd,
"\n");
4273 ao2_cleanup(mod_cfg);
4285 "Usage: cdr submit\n"
4286 "Posts all pending batched CDR data to the configured CDR\n"
4287 "backend engine modules.\n";
4293 return CLI_SHOWUSAGE;
4302 ast_cli(a->fd,
"Cannot submit CDR batch: CDR engine disabled.\n");
4304 ast_cli(a->fd,
"Cannot submit CDR batch: batch mode not enabled.\n");
4307 ast_cli(a->fd,
"Submitted CDRs to backend engines for processing. This may take a while.\n");
4309 ao2_cleanup(mod_cfg);
4315 AST_CLI_DEFINE(handle_cli_submit,
"Posts all pending batched CDR data"),
4316 AST_CLI_DEFINE(handle_cli_status,
"Display the CDR status"),
4317 AST_CLI_DEFINE(handle_cli_show,
"Display active CDRs for channels"),
4318 AST_CLI_DEFINE(handle_cli_debug,
"Enable debugging in the CDR engine"),
4331 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
4337 cdr_all_unlink(cdr);
4342 static void finalize_batch_mode(
void)
4344 if (cdr_thread == AST_PTHREADT_NULL) {
4348 pthread_cancel(cdr_thread);
4349 pthread_kill(cdr_thread, SIGURG);
4350 pthread_join(cdr_thread, NULL);
4351 cdr_thread = AST_PTHREADT_NULL;
4352 ast_cond_destroy(&cdr_pending_cond);
4358 if (!stasis_router) {
4371 channel_subscription = stasis_forward_cancel(channel_subscription);
4372 bridge_subscription = stasis_forward_cancel(bridge_subscription);
4373 parking_subscription = stasis_forward_cancel(parking_subscription);
4385 if (channel_subscription || bridge_subscription || parking_subscription) {
4390 if (!channel_subscription) {
4394 if (!bridge_subscription) {
4398 if (!parking_subscription) {
4421 aco_option_register(&cfg_info,
"size", ACO_EXACT, general_options, DEFAULT_BATCH_SIZE,
OPT_UINT_T, PARSE_IN_RANGE,
FLDSET(
struct ast_cdr_config, batch_settings.size), 0, MAX_BATCH_SIZE);
4422 aco_option_register(&cfg_info,
"time", ACO_EXACT, general_options, DEFAULT_BATCH_TIME,
OPT_UINT_T, PARSE_IN_RANGE,
FLDSET(
struct ast_cdr_config, batch_settings.time), 1, MAX_BATCH_TIME);
4441 ao2_cleanup(mod_cfg);
4444 ast_log(LOG_NOTICE,
"Failed to process CDR configuration; using defaults\n");
4446 cdr_set_debug_mode(mod_cfg);
4447 ao2_cleanup(mod_cfg);
4453 static void cdr_engine_shutdown(
void)
4456 stasis_router = NULL;
4458 ao2_cleanup(cdr_topic);
4465 finalize_batch_mode();
4476 ao2_cleanup(active_cdrs_master);
4477 active_cdrs_master = NULL;
4480 ao2_cleanup(active_cdrs_all);
4481 active_cdrs_all = NULL;
4487 if (cdr_thread == AST_PTHREADT_NULL) {
4488 ast_cond_init(&cdr_pending_cond, NULL);
4489 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
4490 ast_log(LOG_ERROR,
"Unable to start CDR thread.\n");
4498 ast_log(LOG_NOTICE,
"CDR batch mode logging enabled, first of either size %u or time %u seconds.\n",
4499 config->batch_settings.
size, config->batch_settings.
time);
4511 static void cdr_master_print_fn(
void *v_obj,
void *where,
ao2_prnt_fn *prnt)
4519 for (it_cdr = cdr; it_cdr; it_cdr = it_cdr->
next) {
4520 prnt(where,
"Party A: %s; Party B: %s; Bridge %s\n",
4536 static void cdr_all_print_fn(
void *v_obj,
void *where,
ao2_prnt_fn *prnt)
4543 prnt(where,
"Party A: %s; Party B: %s; Bridge %s",
4561 ast_log(AST_LOG_ERROR,
"Failed to create Stasis subscriptions\n");
4562 ao2_cleanup(mod_cfg);
4566 cdr_enable_batch_mode(mod_cfg->
general);
4568 ast_log(LOG_NOTICE,
"CDR simple logging enabled.\n");
4572 ast_log(LOG_NOTICE,
"CDR logging disabled.\n");
4574 ao2_cleanup(mod_cfg);
4576 return mod_cfg ? 0 : -1;
4579 static int unload_module(
void)
4586 static int load_module(
void)
4598 stasis_router = stasis_message_router_create(cdr_topic);
4599 if (!stasis_router) {
4616 dial_changes_ignored = 0;
4618 dial_changes_ignored = 1;
4619 CDR_DEBUG(
"Dial messages will be mostly ignored\n");
4628 CDR_DEBUG(
"All bridge and parking messages will be ignored\n");
4634 ao2_cleanup(mod_cfg);
4636 ast_log(LOG_WARNING,
"Unable to obtain CDR configuration during module load?\n");
4641 if (!active_cdrs_master) {
4648 if (!active_cdrs_all) {
4655 ast_log(LOG_ERROR,
"Unable to create schedule context.\n");
4676 if (cdr_sync_message_type()) {
4680 if (!stasis_router) {
4685 payload = ao2_alloc(
sizeof(*payload), NULL);
4690 ast_debug(1,
"CDR Engine termination request received; waiting on messages...\n");
4696 ao2_cleanup(message);
4697 ao2_cleanup(payload);
4705 static int reload_module(
void)
4713 ao2_cleanup(old_mod_cfg);
4723 finalize_batch_mode();
4726 ao2_cleanup(mod_cfg);
4728 ao2_cleanup(old_mod_cfg);
4732 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"CDR Engine",
4733 .support_level = AST_MODULE_SUPPORT_CORE,
4734 .load = load_module,
4735 .unload = unload_module,
4736 .reload = reload_module,
4738 .requires =
"extconfig",
static struct stasis_message_router * stasis_router
Message router for stasis messages regarding channel state.
const ast_string_field data
static long cdr_object_get_billsec(struct cdr_object *cdr)
Compute the billsec for a cdr_object.
struct ao2_container * channels
char accountcode[AST_MAX_ACCOUNT_CODE]
static int cdr_object_update_party_b_userfield_cb(void *obj, void *arg, void *data, int flags)
Callback used to update the userfield on Party B on all CDRs.
void( ao2_prnt_fn)(void *where, const char *fmt,...)
Print output.
struct ast_channel_snapshot_base * base
Asterisk locking-related definitions:
Channels with this particular technology are an implementation detail of Asterisk and should generall...
static void * module_config_alloc(void)
Create a new module config object.
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
struct varshead variables
int ast_cdr_reset(const char *channel_name, int keep_variables)
Reset the detail record.
static void reset_batch(void)
struct ast_cdr_config * ast_cdr_get_config(void)
Obtain the current CDR configuration.
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
struct ast_channel_snapshot * channel
char dstchannel[AST_MAX_EXTENSION]
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
int(*const process_parked_channel)(struct cdr_object *cdr, struct ast_parked_call_payload *parking_info)
Process an update informing us that the channel got itself parked.
const ast_string_field name
static void bridge_candidate_process(struct cdr_object *cdr, struct cdr_object *base_cand_cdr)
Process a single bridge_candidate.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
The arg parameter is a search key, but is not an object.
struct ast_flags settings
static void cdr_object_format_var_internal(struct cdr_object *cdr, const char *name, char *value, size_t length)
Format a variable on a cdr_object.
int stasis_message_router_set_congestion_limits(struct stasis_message_router *router, long low_water, long high_water)
Set the high and low alert water marks of the stasis message router.
struct ast_channel_snapshot * snapshot
static const char * ignore_categories[]
static void handle_bridge_leave_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Handler for when a channel leaves a bridge.
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
int ast_cdr_set_property(const char *channel_name, enum ast_cdr_options option)
Set a property on a CDR for a channel.
static void handle_parking_bridge_enter_message(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel, const struct timeval *event_time)
Handle entering into a parking bridge.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
struct stasis_message_type * ast_parked_call_type(void)
accessor for the parked call stasis message type
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
static void cdr_object_dtor(void *obj)
cdr_object Destructor
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
struct ast_cdr * ast_cdr_alloc(void)
Allocate a CDR record.
Structure that contains a snapshot of information about a bridge.
#define aco_option_register(info, name, matchtype, types, default_val, opt_type, flags,...)
Register a config option.
char dcontext[AST_MAX_EXTENSION]
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
static int process_config(int reload)
Load (or reload) configuration.
static void destroy_subscriptions(void)
Destroy the active Stasis subscriptions.
struct cdr_object_snapshot party_b
Structure representing a snapshot of channel state.
int(*const process_party_a)(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
Process a Party A update for the cdr_object.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
A wrapper object around a snapshot. Fields that are mutable by the CDR engine are replicated here...
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Assume that the ao2_container is already locked.
int ast_sched_add_variable(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data, int variable) attribute_warn_unused_result
Adds a scheduled event with rescheduling support.
static struct stasis_topic * cdr_topic
The parent topic for all topics we want to aggregate for CDRs.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
const ast_string_field accountcode
const ast_string_field exten
static struct cdr_object * cdr_object_alloc(struct ast_channel_snapshot *chan, const struct timeval *event_time)
cdr_object constructor
#define AST_NUM_CHANNEL_BUCKETS
int(* ast_cdrbe)(struct ast_cdr *cdr)
CDR backend callback.
const char * ast_channel_amaflags2string(enum ama_flags flags)
Convert the enum representation of an AMA flag to a string representation.
const ast_string_field uniqueid
struct stasis_topic * ast_bridge_topic_all(void)
A topic which publishes the events for all bridges.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
enum aco_process_status aco_process_config(struct aco_info *info, int reload)
Process a config info via the options registered with an aco_info.
static void handle_dial_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Handler for Stasis-Core dial messages.
#define ao2_global_obj_ref(holder)
Get a reference to the object stored in the global holder.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
CONFIG_INFO_CORE("stasis", cfg_info, globals, stasis_config_alloc,.files=ACO_FILES(&stasis_conf),)
Register information about the configs being processed by this module.
static void cdr_object_set_disposition(struct cdr_object *cdr, int hangupcause)
Set the disposition on a cdr_object based on a hangupcause code.
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Type for default option handler for bools (ast_true/ast_false) that are stored in a flag...
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
A parked call message payload.
#define ast_strdup(str)
A wrapper for strdup()
static int copy_variables(struct varshead *to_list, struct varshead *from_list)
Copy variables from one list to another.
static long cdr_object_get_duration(struct cdr_object *cdr)
The representation of a single configuration file to be processed.
ast_cdr_options
CDR manipulation options. Certain function calls will manipulate the state of a CDR object based on t...
Structure representing a change of snapshot of channel state.
enum ast_parked_call_event_type event_type
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define ACO_TYPES(...)
A helper macro to ensure that aco_info types always have a sentinel.
static void handle_channel_snapshot_update_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Handler for channel snapshot update messages.
struct ast_channel_snapshot_dialplan * dialplan
Registration object for CDR backends.
struct cdr_object_snapshot party_a
void stasis_message_router_unsubscribe_and_join(struct stasis_message_router *router)
Unsubscribe the router from the upstream topic, blocking until the final message has been processed...
void(*const init_function)(struct cdr_object *cdr)
An initialization function. This will be called automatically when a cdr_object is switched to this t...
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
void ast_cdr_engine_term(void)
struct ast_json * ast_multi_channel_blob_get_json(struct ast_multi_channel_blob *obj)
Retrieve the JSON blob from a ast_multi_channel_blob. Returned ast_json is still owned by obj...
enum process_bridge_enter_results(*const process_bridge_enter)(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
Process the entering of a bridge by this CDR. The purpose of this callback is to have the CDR prepare...
A virtual table used for cdr_object.
static struct stasis_forward * bridge_subscription
Our subscription for bridges.
const ast_string_field appl
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
const ast_string_field context
static int cdr_object_dispatch_all_cb(void *obj, void *arg, int flags)
This dispatches all cdr_object. It should only be used during shutdown, so that we get billing record...
const ast_string_field dialed_subaddr
char lastdata[AST_MAX_EXTENSION]
const ast_string_field context
Configuration File Parser.
void ao2_container_unregister(const char *name)
Unregister a container for CLI stats and integrity check.
struct ast_bridge_snapshot * bridge
static int global_cdr_sequence
The global sequence counter used for CDRs.
struct stasis_topic * ast_channel_topic_all(void)
A topic which publishes the events for all channels.
int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
Register a container for CLI stats and integrity check.
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
char userfield[AST_MAX_USER_FIELD]
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
const ast_string_field technology
void ast_cdr_free(struct ast_cdr *cdr)
Free a CDR record.
int aco_info_init(struct aco_info *info)
Initialize an aco_info structure.
struct ast_channel_snapshot * parkee
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
static int create_subscriptions(void)
Create the Stasis subcriptions for CDRs.
#define AST_SCHED_DEL(sched, id)
Remove a scheduler entry.
int(*const process_dial_begin)(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer)
Process the beginning of a dial. A dial message implies one of two things: The cdr_object's Party A h...
const ast_string_field appl
Type for default option handler for unsigned integers.
static int cdr_object_format_property(struct cdr_object *cdr_obj, const char *name, char *value, size_t length)
Format one of the standard properties on a cdr_object.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_strdupa(s)
duplicate a string in memory from the stack
static void cdr_object_dispatch(struct cdr_object *cdr)
Dispatch a CDR.
const struct timeval * stasis_message_timestamp(const struct stasis_message *msg)
Get the time when a message was created.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
process_bridge_enter_results
Return types for process_bridge_enter functions.
Scheduler Routines (derived from cheops)
char linkedid[AST_MAX_UNIQUEID]
#define AST_STRING_FIELD(name)
Declare a string field.
const ast_string_field linkedid
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
static ast_mutex_t cdr_pending_lock
These are used to wake up the CDR thread when there's work to do.
static void module_config_destructor(void *obj)
Dispose of a module config object.
static struct aco_file module_file_conf
The file definition.
const ast_string_field subaddr
int ast_register_atexit(void(*func)(void))
Register a function to be executed before Asterisk exits.
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
struct cdr_object_fn_table parked_state_fn_table
The virtual table for the Parked state.
const ast_string_field exten
struct timeval creationtime
static int filter_bridge_messages(struct ast_bridge_snapshot *bridge)
Filter bridge messages based on bridge technology.
static void handle_bridge_pairings(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge)
Handle creating bridge pairings for the cdr_object that just entered a bridge.
char uniqueid[AST_MAX_UNIQUEID]
struct ast_channel_snapshot_hangup * hangup
char dst[AST_MAX_EXTENSION]
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
static int init_batch(void)
struct cdr_object_fn_table dialed_pending_state_fn_table
The virtual table for the Dialed Pending state.
A set of macros to manage forward-linked lists.
#define ast_malloc(len)
A wrapper for malloc()
The configuration settings for this module.
#define ast_debug(level,...)
Log a DEBUG message.
void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
Set CDR user field for channel (stored in CDR)
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Blob of data associated with a bridge.
enum aco_category_op category_match
static AO2_GLOBAL_OBJ_STATIC(module_configs)
The container for the module configuration.
static void free_variables(struct varshead *headp)
Delete all variables from a variable list.
static int snapshot_cep_changed(struct ast_channel_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot)
Return whether or not a channel has changed its state in the dialplan, subject to endbeforehexten log...
struct cdr_object_fn_table * fn_table
Their was an error and no changes were applied.
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
enum ast_cdr_disposition disposition
int ast_cdr_getvar(const char *channel_name, const char *name, char *value, size_t length)
Retrieve a CDR variable from a channel's current CDR.
struct ast_channel_snapshot_caller * caller
List of registered modifiers.
static struct cdr_object_snapshot * cdr_object_pick_party_a(struct cdr_object_snapshot *left, struct cdr_object_snapshot *right)
Given two CDR snapshots, figure out who should be Party A for the resulting CDR.
An in-memory representation of an active CDR.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static void cdr_object_transition_state_init(struct cdr_object *cdr, struct cdr_object_fn_table *fn_table, int do_init)
Transition a cdr_object to a new state with initiation flag.
Queued CDR waiting to be batched.
struct ast_flags softhangup_flags
Responsible for call detail data.
Configuration option-handling.
char lastapp[AST_MAX_EXTENSION]
const ast_string_field data
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
int ast_cdr_backend_unsuspend(const char *name)
Unsuspend a CDR backend.
struct cdr_object_fn_table single_state_fn_table
The virtual table for the Single state.
Support for dynamic strings.
const ast_string_field dnid
#define ao2_unlink(container, obj)
Remove an object from a container.
static struct ast_cdr * cdr_object_create_public_records(struct cdr_object *cdr)
Create a chain of ast_cdr objects from a chain of cdr_object suitable for consumption by the register...
static ast_mutex_t cdr_batch_lock
Lock protecting modifications to the batch queue.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
void aco_info_destroy(struct aco_info *info)
Destroy an initialized aco_info struct.
int ast_cdr_fork(const char *channel_name, struct ast_flags *options)
Fork a CDR.
int ast_cdr_serialize_variables(const char *channel_name, struct ast_str **buf, char delim, char sep)
Serializes all the data and variables for a current CDR record.
unsigned int parkingspace
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
#define ao2_global_obj_release(holder)
Release the ao2 object held in the global holder.
static void cdr_object_check_party_a_hangup(struct cdr_object *cdr)
Check to see if a CDR needs to move to the finalized state because its Party A hungup.
int ast_cdr_clear_property(const char *channel_name, enum ast_cdr_options option)
Clear a property on a CDR for a channel.
const char * name
Name of the subclass.
static struct ao2_container * active_cdrs_master
A container of the active master CDRs indexed by Party A channel uniqueid.
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
void(*const process_party_b)(struct cdr_object *cdr, struct ast_channel_snapshot *snapshot)
Process a Party B update for the cdr_object.
static int single_state_bridge_enter_comparison(struct cdr_object *cdr, struct cdr_object *cand_cdr)
Handle a comparison between our cdr_object and a cdr_object already in the bridge while in the Single...
struct stasis_message_router * ast_cdr_message_router(void)
Return the message router for the CDR engine.
const ast_string_field party_b_name
long ast_sched_when(struct ast_sched_context *con, int id)
Returns the number of seconds before an event takes place.
static int snapshot_is_dialed(struct ast_channel_snapshot *snapshot)
Return whether or not a ast_channel_snapshot is for a channel that was created as the result of a dia...
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define SCOPED_AO2LOCK(varname, obj)
scoped lock specialization for ao2 mutexes.
#define ast_calloc(num, len)
A wrapper for calloc()
static void start_batch_mode(void)
const ast_string_field name
int aco_set_defaults(struct aco_type *type, const char *category, void *obj)
Set all default options of obj.
Module could not be loaded properly.
struct ast_channel_snapshot * new_snapshot
static void cdr_object_snapshot_copy(struct cdr_object_snapshot *dst, struct cdr_object_snapshot *src)
Copy a snapshot and its details.
enum ast_channel_state state
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
const ast_string_field uniqueid
An API for managing task processing threads that can be shared across modules.
const ast_string_field parkinglot
struct stasis_message_type * ast_channel_snapshot_type(void)
Message type for ast_channel_snapshot_update.
int(*const process_parking_bridge_enter)(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
Process entering into a parking bridge.
int ast_cdr_modifier_unregister(const char *name)
Unregister a CDR modifier.
struct ast_channel_snapshot * old_snapshot
Structure used to handle boolean flags.
char src[AST_MAX_EXTENSION]
char peeraccount[AST_MAX_ACCOUNT_CODE]
void ast_cdr_set_config(struct ast_cdr_config *config)
Set the current CDR configuration.
struct cdr_object_fn_table finalized_state_fn_table
The virtual table for the finalized state.
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
#define ao2_global_obj_replace_unref(holder, obj)
Replace an ao2 object in the global holder, throwing away any old object.
STASIS_MESSAGE_TYPE_DEFN_LOCAL(cdr_sync_message_type)
A message type used to synchronize with the CDR topic.
The arg parameter is an object of the same type.
struct stasis_topic * ast_parking_topic(void)
accessor for the parking stasis topic
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
static int cdr_object_party_b_left_bridge_cb(void *obj, void *arg, void *data, int flags)
Callback used to notify CDRs of a Party B leaving the bridge.
struct ast_flags settings
#define ao2_replace(dst, src)
Replace one object reference with another cleaning up the original.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
void stasis_message_router_publish_sync(struct stasis_message_router *router, struct stasis_message *message)
Publish a message to a message router's subscription synchronously.
int ast_cdr_is_enabled(void)
Return TRUE if CDR subsystem is enabled.
Standard Command Line Interface.
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
ast_cdr_disposition
CDR Flags - Disposition.
Type information about a category-level configurable object.
const ast_string_field number
int ast_cdr_modifier_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR modifier.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
int ast_cdr_backend_suspend(const char *name)
Suspend a CDR backend temporarily.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
static void handle_parked_call_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Handler for when a channel is parked.
struct ast_channel_snapshot * ast_multi_channel_blob_get_channel(struct ast_multi_channel_blob *obj, const char *role)
Retrieve a channel snapshot associated with a specific role from a ast_multi_channel_blob.
static struct aco_type general_option
The type definition for general options.
A multi channel blob data structure for multi_channel_blob stasis messages.
int ast_cdr_setvar(const char *channel_name, const char *name, const char *value)
Set a variable on a CDR.
static void handle_standard_bridge_enter_message(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel, const struct timeval *event_time)
Handle a bridge enter message for a 'normal' bridge.
static char * cli_complete_show(struct ast_cli_args *a)
Complete user input for 'cdr show'.
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
static struct ao2_container * active_cdrs_all
A container of all active CDRs with a Party B indexed by Party B channel name.
#define ao2_unlink_flags(container, obj, flags)
Remove an object from a container.
static void cdr_object_check_party_a_answer(struct cdr_object *cdr)
Check to see if a CDR needs to be answered based on its Party A. Note that this is safe to call as mu...
Internal Asterisk hangup causes.
Abstract JSON element (object, array, string, int, ...).
int(*const process_bridge_leave)(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
Process the leaving of a bridge by this CDR.
static void cdr_object_swap_snapshot(struct cdr_object_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot)
Swap an old cdr_object_snapshot's ast_channel_snapshot for a new ast_channel_snapshot.
The global options available for CDRs.
struct stasis_message_type * ast_channel_dial_type(void)
Message type for when a channel dials another channel.
static void cdr_object_update_cid(struct cdr_object_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot)
Set Caller ID information on a CDR.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
static struct stasis_forward * channel_subscription
Our subscription for channels.
struct cdr_object_fn_table bridge_state_fn_table
The virtual table for the Bridged state.
char clid[AST_MAX_EXTENSION]
Search option field mask.
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
const ast_string_field uniqueid
struct cdr_object_fn_table dial_state_fn_table
The virtual table for the Dial state.
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
struct ast_channel_snapshot_peer * peer
static struct cdr_object * cdr_object_create_and_append(struct cdr_object *cdr, const struct timeval *event_time)
Create a new cdr_object and append it to an existing chain.
#define ASTERISK_GPL_KEY
The text the key() function should return.
struct ast_cdr_config * general
int(*const process_dial_end)(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
Process the end of a dial. At the end of a dial, a CDR can be transitioned into one of two states - D...
static int check_new_cdr_needed(struct ast_channel_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot)
Determine if we need to add a new CDR based on snapshots.
Asterisk module definitions.
static void cdr_object_finalize(struct cdr_object *cdr)
Finalize a CDR.
const ast_string_field subclass
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
static struct stasis_forward * parking_subscription
Our subscription for parking.
static void handle_cdr_sync_message(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Handler for a synchronization message.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static int cdr_toggle_runtime_options(void)
Checks if CDRs are enabled and enables/disables the necessary options.
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
List of registered backends.
static force_inline int attribute_pure ast_str_case_hash(const char *str)
Compute a hash value on a case-insensitive string.
const ast_string_field bridge
const ast_string_field name
void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int raw)
Format a CDR variable from an already posted CDR.
char userfield[AST_MAX_USER_FIELD]
static void cdr_object_transition_state(struct cdr_object *cdr, struct cdr_object_fn_table *fn_table)
Transition a cdr_object to a new state.
const char * ast_cdr_disp2str(int disposition)
Disposition to a string.
static struct ast_sched_context * sched
Scheduler items.
struct ast_cdr * ast_cdr_dup(struct ast_cdr *cdr)
Duplicate a public CDR.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define AST_MAX_USER_FIELD
#define ao2_link(container, obj)
Add an object to a container.