Update Asterisk's CDRs for the new bridging framework

This patch is the initial push to update Asterisk's CDR engine for the new
bridging framework. This patch guts the existing CDR engine and builds the new
on top of messages coming across Stasis. As changes in channel state and bridge
state are detected, CDRs are built and dispatched accordingly. This
fundamentally changes CDRs in a few ways.
(1) CDRs are now *very* reflective of the actual state of channels and bridges.
    This means CDRs track well with what an actual channel is doing - which
    is useful in transfer scenarios (which were previously difficult to pin
    down). It does, however, mean that CDRs cannot be 'fooled'. Previous
    behavior in Asterisk allowed for CDR applications, channels, and other
    properties to be spoofed in parts of the code - this no longer works.
(2) CDRs have defined behavior in multi-party scenarios. This behavior will not
    be what everyone wants, but it is a defined behavior and as such, it is
    predictable.
(3) The CDR manipulation functions and applications have been overhauled. Major
    changes have been made to ResetCDR and ForkCDR in particular. Many of the
    options for these two applications no longer made any sense with the new
    framework and the (slightly) more immutable nature of CDRs.

There are a plethora of other changes. For a full description of CDR behavior,
see the CDR specification on the Asterisk wiki.

(closes issue ASTERISK-21196)

Review: https://reviewboard.asterisk.org/r/2486/



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@391947 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Matthew Jordan
2013-06-17 03:00:38 +00:00
parent 67e35c7b47
commit 6258bbe7bd
68 changed files with 7207 additions and 2745 deletions

View File

@@ -433,7 +433,7 @@ static int odbc_log(struct ast_cdr *cdr)
ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
colptr = colbuf;
} else {
ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
ast_cdr_format_var(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), datefield ? 0 : 1);
}
if (colptr) {
@@ -472,9 +472,9 @@ static int odbc_log(struct ast_cdr *cdr)
* form (but only when we're dealing with a character-based field).
*/
if (strcasecmp(entry->name, "disposition") == 0) {
ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
ast_cdr_format_var(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0);
} else if (strcasecmp(entry->name, "amaflags") == 0) {
ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
ast_cdr_format_var(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0);
}
/* Truncate too-long fields */

View File

@@ -234,7 +234,7 @@ static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
/* Disposition */
append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
/* AMA Flags */
append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
append_string(buf, ast_channel_amaflags2string(cdr->amaflags), bufsize);
/* Unique ID */
if (loguniqueid)
append_string(buf, cdr->uniqueid, bufsize);
@@ -285,9 +285,6 @@ static int csv_log(struct ast_cdr *cdr)
char buf[1024];
char csvmaster[PATH_MAX];
snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
#if 0
printf("[CDR] %s ('%s' -> '%s') Dur: %ds Bill: %ds Disp: %s Flags: %s Account: [%s]\n", cdr->channel, cdr->src, cdr->dst, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->accountcode);
#endif
if (build_csv_record(buf, sizeof(buf), cdr)) {
ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes. CDR not recorded!\n", (int)sizeof(buf));
return 0;

View File

@@ -67,20 +67,20 @@ AST_THREADSTORAGE(custom_buf);
static const char name[] = "cdr-custom";
struct cdr_config {
struct cdr_custom_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(filename);
AST_STRING_FIELD(format);
);
ast_mutex_t lock;
AST_RWLIST_ENTRY(cdr_config) list;
AST_RWLIST_ENTRY(cdr_custom_config) list;
};
static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
static AST_RWLIST_HEAD_STATIC(sinks, cdr_custom_config);
static void free_config(void)
{
struct cdr_config *sink;
struct cdr_custom_config *sink;
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
ast_free(sink);
@@ -103,7 +103,7 @@ static int load_config(void)
var = ast_variable_browse(cfg, "mappings");
while (var) {
if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
struct cdr_config *sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
struct cdr_custom_config *sink = ast_calloc_with_stringfields(1, struct cdr_custom_config, 1024);
if (!sink) {
ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
@@ -130,7 +130,7 @@ static int custom_log(struct ast_cdr *cdr)
{
struct ast_channel *dummy;
struct ast_str *str;
struct cdr_config *config;
struct cdr_custom_config *config;
/* Batching saves memory management here. Otherwise, it's the same as doing an allocation and free each time. */
if (!(str = ast_str_thread_get(&custom_buf, 16))) {

View File

@@ -203,7 +203,7 @@ static int manager_log(struct ast_cdr *cdr)
cdr->accountcode, cdr->src, cdr->dst, cdr->dcontext, cdr->clid, cdr->channel,
cdr->dstchannel, cdr->lastapp, cdr->lastdata, strStartTime, strAnswerTime, strEndTime,
cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition),
ast_cdr_flags2str(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf);
ast_channel_amaflags2string(cdr->amaflags), cdr->uniqueid, cdr->userfield,buf);
return 0;
}

View File

@@ -124,10 +124,13 @@ static SQLHSTMT execute_cb(struct odbc_obj *obj, void *data)
SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->billsec, 0, NULL);
}
if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING))
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ast_cdr_disp2str(cdr->disposition)) + 1, 0, ast_cdr_disp2str(cdr->disposition), 0, NULL);
else
if (ast_test_flag(&config, CONFIG_DISPOSITIONSTRING)) {
char *disposition;
disposition = ast_strdupa(ast_cdr_disp2str(cdr->disposition));
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(disposition) + 1, 0, disposition, 0, NULL);
} else {
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->disposition, 0, NULL);
}
SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &cdr->amaflags, 0, NULL);
SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, sizeof(cdr->accountcode), 0, cdr->accountcode, 0, NULL);

View File

@@ -222,9 +222,9 @@ static int pgsql_log(struct ast_cdr *cdr)
AST_RWLIST_RDLOCK(&psql_columns);
AST_RWLIST_TRAVERSE(&psql_columns, cur, list) {
/* For fields not set, simply skip them */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
if (strcmp(cur->name, "calldate") == 0 && !value) {
ast_cdr_getvar(cdr, "start", &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, "start", &value, buf, sizeof(buf), 0);
}
if (!value) {
if (cur->notnull && !cur->hasdefault) {
@@ -286,7 +286,7 @@ static int pgsql_log(struct ast_cdr *cdr)
} else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
if (cur->type[0] == 'i') {
/* Get integer, no need to escape anything */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
LENGTHEN_BUF2(13);
ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
} else if (strncmp(cur->type, "float", 5) == 0) {
@@ -302,18 +302,18 @@ static int pgsql_log(struct ast_cdr *cdr)
} else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
if (strncmp(cur->type, "int", 3) == 0) {
/* Integer, no need to escape anything */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 1);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 1);
LENGTHEN_BUF2(13);
ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
} else {
/* Although this is a char field, there are no special characters in the values for these fields */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
LENGTHEN_BUF2(31);
ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
}
} else {
/* Arbitrary field, could be anything */
ast_cdr_getvar(cdr, cur->name, &value, buf, sizeof(buf), 0, 0);
ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
if (strncmp(cur->type, "int", 3) == 0) {
long long whatever;
if (value && sscanf(value, "%30lld", &whatever) == 1) {

View File

@@ -170,12 +170,12 @@ static int build_radius_record(VALUE_PAIR **tosend, struct ast_cdr *cdr)
return -1;
/* Disposition */
tmp = ast_cdr_disp2str(cdr->disposition);
tmp = ast_strdupa(ast_cdr_disp2str(cdr->disposition));
if (!rc_avpair_add(rh, tosend, PW_AST_DISPOSITION, tmp, strlen(tmp), VENDOR_CODE))
return -1;
/* AMA Flags */
tmp = ast_cdr_flags2str(cdr->amaflags);
tmp = ast_strdupa(ast_channel_amaflags2string(cdr->amaflags));
if (!rc_avpair_add(rh, tosend, PW_AST_AMA_FLAGS, tmp, strlen(tmp), VENDOR_CODE))
return -1;

View File

@@ -60,7 +60,7 @@ AST_THREADSTORAGE(syslog_buf);
static const char name[] = "cdr-syslog";
struct cdr_config {
struct cdr_syslog_config {
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(ident);
AST_STRING_FIELD(format);
@@ -68,14 +68,14 @@ struct cdr_config {
int facility;
int priority;
ast_mutex_t lock;
AST_LIST_ENTRY(cdr_config) list;
AST_LIST_ENTRY(cdr_syslog_config) list;
};
static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
static void free_config(void)
{
struct cdr_config *sink;
struct cdr_syslog_config *sink;
while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
ast_mutex_destroy(&sink->lock);
ast_free(sink);
@@ -86,7 +86,7 @@ static int syslog_log(struct ast_cdr *cdr)
{
struct ast_channel *dummy;
struct ast_str *str;
struct cdr_config *sink;
struct cdr_syslog_config *sink;
/* Batching saves memory management here. Otherwise, it's the same as doing an
allocation and free each time. */
@@ -174,7 +174,7 @@ static int load_config(int reload)
}
while ((catg = ast_category_browse(cfg, catg))) {
struct cdr_config *sink;
struct cdr_syslog_config *sink;
if (!strcasecmp(catg, "general")) {
continue;
@@ -186,7 +186,7 @@ static int load_config(int reload)
continue;
}
sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
sink = ast_calloc_with_stringfields(1, struct cdr_syslog_config, 1024);
if (!sink) {
ast_log(AST_LOG_ERROR,

View File

@@ -176,7 +176,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, hrduration,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid,
userfield
);
} else {
@@ -196,7 +196,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, cdr->duration,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid,
userfield
);
}
@@ -226,7 +226,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, hrduration,
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
hrbillsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid
);
} else {
erc = dbfcmd(settings->dbproc,
@@ -245,7 +245,7 @@ retry:
settings->table,
accountcode, src, dst, dcontext, clid, channel,
dstchannel, lastapp, lastdata, start, answer, end, cdr->duration,
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), uniqueid
cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_channel_amaflags2string(cdr->amaflags), uniqueid
);
}
}