00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "asterisk.h"
00039
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 371989 $")
00041
00042 #include <signal.h>
00043
00044 #include "asterisk/lock.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/cdr.h"
00047 #include "asterisk/callerid.h"
00048 #include "asterisk/manager.h"
00049 #include "asterisk/causes.h"
00050 #include "asterisk/linkedlists.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/sched.h"
00053 #include "asterisk/config.h"
00054 #include "asterisk/cli.h"
00055 #include "asterisk/stringfields.h"
00056 #include "asterisk/data.h"
00057
00058
00059
00060
00061
00062 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
00063 char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
00064
00065 struct ast_cdr_beitem {
00066 char name[20];
00067 char desc[80];
00068 ast_cdrbe be;
00069 AST_RWLIST_ENTRY(ast_cdr_beitem) list;
00070 };
00071
00072 static AST_RWLIST_HEAD_STATIC(be_list, ast_cdr_beitem);
00073
00074 struct ast_cdr_batch_item {
00075 struct ast_cdr *cdr;
00076 struct ast_cdr_batch_item *next;
00077 };
00078
00079 static struct ast_cdr_batch {
00080 int size;
00081 struct ast_cdr_batch_item *head;
00082 struct ast_cdr_batch_item *tail;
00083 } *batch = NULL;
00084
00085
00086 static int cdr_sequence = 0;
00087
00088 static int cdr_seq_inc(struct ast_cdr *cdr);
00089
00090 static struct ast_sched_context *sched;
00091 static int cdr_sched = -1;
00092 static pthread_t cdr_thread = AST_PTHREADT_NULL;
00093
00094 static int enabled;
00095 static const int ENABLED_DEFAULT = 1;
00096
00097 static int batchmode;
00098 static const int BATCHMODE_DEFAULT = 0;
00099
00100 static int unanswered;
00101 static const int UNANSWERED_DEFAULT = 0;
00102
00103 static int congestion;
00104 static const int CONGESTION_DEFAULT = 0;
00105
00106 static int batchsize;
00107 static const int BATCH_SIZE_DEFAULT = 100;
00108
00109 static int batchtime;
00110 static const int BATCH_TIME_DEFAULT = 300;
00111
00112 static int batchscheduleronly;
00113 static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
00114
00115 static int batchsafeshutdown;
00116 static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
00117
00118 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
00119
00120
00121 AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
00122 static ast_cond_t cdr_pending_cond;
00123
00124 int check_cdr_enabled(void)
00125 {
00126 return enabled;
00127 }
00128
00129
00130
00131
00132
00133
00134 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
00135 {
00136 struct ast_cdr_beitem *i = NULL;
00137
00138 if (!name)
00139 return -1;
00140
00141 if (!be) {
00142 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00143 return -1;
00144 }
00145
00146 AST_RWLIST_WRLOCK(&be_list);
00147 AST_RWLIST_TRAVERSE(&be_list, i, list) {
00148 if (!strcasecmp(name, i->name)) {
00149 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00150 AST_RWLIST_UNLOCK(&be_list);
00151 return -1;
00152 }
00153 }
00154
00155 if (!(i = ast_calloc(1, sizeof(*i))))
00156 return -1;
00157
00158 i->be = be;
00159 ast_copy_string(i->name, name, sizeof(i->name));
00160 ast_copy_string(i->desc, desc, sizeof(i->desc));
00161
00162 AST_RWLIST_INSERT_HEAD(&be_list, i, list);
00163 AST_RWLIST_UNLOCK(&be_list);
00164
00165 return 0;
00166 }
00167
00168
00169 void ast_cdr_unregister(const char *name)
00170 {
00171 struct ast_cdr_beitem *i = NULL;
00172
00173 AST_RWLIST_WRLOCK(&be_list);
00174 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
00175 if (!strcasecmp(name, i->name)) {
00176 AST_RWLIST_REMOVE_CURRENT(list);
00177 break;
00178 }
00179 }
00180 AST_RWLIST_TRAVERSE_SAFE_END;
00181 AST_RWLIST_UNLOCK(&be_list);
00182
00183 if (i) {
00184 ast_verb(2, "Unregistered '%s' CDR backend\n", name);
00185 ast_free(i);
00186 }
00187 }
00188
00189 int ast_cdr_isset_unanswered(void)
00190 {
00191 return unanswered;
00192 }
00193
00194 int ast_cdr_isset_congestion(void)
00195 {
00196 return congestion;
00197 }
00198
00199 struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr)
00200 {
00201 struct ast_cdr *newcdr = ast_cdr_dup(cdr);
00202 if (!newcdr)
00203 return NULL;
00204
00205 cdr_seq_inc(newcdr);
00206 return newcdr;
00207 }
00208
00209 struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr)
00210 {
00211 struct ast_cdr *newcdr = ast_cdr_dup(cdr);
00212 if (!newcdr)
00213 return NULL;
00214
00215 cdr_seq_inc(cdr);
00216 return newcdr;
00217 }
00218
00219
00220
00221
00222 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
00223 {
00224 struct ast_cdr *newcdr;
00225
00226 if (!cdr)
00227 return NULL;
00228 newcdr = ast_cdr_alloc();
00229 if (!newcdr)
00230 return NULL;
00231
00232 memcpy(newcdr, cdr, sizeof(*newcdr));
00233
00234 memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
00235 ast_cdr_copy_vars(newcdr, cdr);
00236 newcdr->next = NULL;
00237
00238 return newcdr;
00239 }
00240
00241 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
00242 {
00243 if (ast_strlen_zero(name))
00244 return NULL;
00245
00246 for (; cdr; cdr = recur ? cdr->next : NULL) {
00247 struct ast_var_t *variables;
00248 struct varshead *headp = &cdr->varshead;
00249 AST_LIST_TRAVERSE(headp, variables, entries) {
00250 if (!strcasecmp(name, ast_var_name(variables)))
00251 return ast_var_value(variables);
00252 }
00253 }
00254
00255 return NULL;
00256 }
00257
00258 static void cdr_get_tv(struct timeval when, const char *fmt, char *buf, int bufsize)
00259 {
00260 if (fmt == NULL) {
00261 snprintf(buf, bufsize, "%ld.%06ld", (long)when.tv_sec, (long)when.tv_usec);
00262 } else {
00263 if (when.tv_sec) {
00264 struct ast_tm tm;
00265
00266 ast_localtime(&when, &tm, NULL);
00267 ast_strftime(buf, bufsize, fmt, &tm);
00268 }
00269 }
00270 }
00271
00272
00273 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
00274 {
00275 const char *fmt = "%Y-%m-%d %T";
00276 const char *varbuf;
00277
00278 if (!cdr)
00279 return;
00280
00281 *ret = NULL;
00282
00283
00284
00285 if (!strcasecmp(name, "clid"))
00286 ast_copy_string(workspace, cdr->clid, workspacelen);
00287 else if (!strcasecmp(name, "src"))
00288 ast_copy_string(workspace, cdr->src, workspacelen);
00289 else if (!strcasecmp(name, "dst"))
00290 ast_copy_string(workspace, cdr->dst, workspacelen);
00291 else if (!strcasecmp(name, "dcontext"))
00292 ast_copy_string(workspace, cdr->dcontext, workspacelen);
00293 else if (!strcasecmp(name, "channel"))
00294 ast_copy_string(workspace, cdr->channel, workspacelen);
00295 else if (!strcasecmp(name, "dstchannel"))
00296 ast_copy_string(workspace, cdr->dstchannel, workspacelen);
00297 else if (!strcasecmp(name, "lastapp"))
00298 ast_copy_string(workspace, cdr->lastapp, workspacelen);
00299 else if (!strcasecmp(name, "lastdata"))
00300 ast_copy_string(workspace, cdr->lastdata, workspacelen);
00301 else if (!strcasecmp(name, "start"))
00302 cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
00303 else if (!strcasecmp(name, "answer"))
00304 cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
00305 else if (!strcasecmp(name, "end"))
00306 cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
00307 else if (!strcasecmp(name, "duration")) {
00308 snprintf(workspace, workspacelen, "%ld", cdr->end.tv_sec != 0 ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
00309 } else if (!strcasecmp(name, "billsec"))
00310 snprintf(workspace, workspacelen, "%ld", cdr->billsec || cdr->answer.tv_sec == 0 ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
00311 else if (!strcasecmp(name, "disposition")) {
00312 if (raw) {
00313 snprintf(workspace, workspacelen, "%ld", cdr->disposition);
00314 } else {
00315 ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
00316 }
00317 } else if (!strcasecmp(name, "amaflags")) {
00318 if (raw) {
00319 snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
00320 } else {
00321 ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
00322 }
00323 } else if (!strcasecmp(name, "accountcode"))
00324 ast_copy_string(workspace, cdr->accountcode, workspacelen);
00325 else if (!strcasecmp(name, "peeraccount"))
00326 ast_copy_string(workspace, cdr->peeraccount, workspacelen);
00327 else if (!strcasecmp(name, "uniqueid"))
00328 ast_copy_string(workspace, cdr->uniqueid, workspacelen);
00329 else if (!strcasecmp(name, "linkedid"))
00330 ast_copy_string(workspace, cdr->linkedid, workspacelen);
00331 else if (!strcasecmp(name, "userfield"))
00332 ast_copy_string(workspace, cdr->userfield, workspacelen);
00333 else if (!strcasecmp(name, "sequence"))
00334 snprintf(workspace, workspacelen, "%d", cdr->sequence);
00335 else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
00336 ast_copy_string(workspace, varbuf, workspacelen);
00337 else
00338 workspace[0] = '\0';
00339
00340 if (!ast_strlen_zero(workspace))
00341 *ret = workspace;
00342 }
00343
00344
00345 static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
00346 "lastapp", "lastdata", "start", "answer", "end", "duration",
00347 "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
00348 "userfield", "sequence", NULL };
00349
00350
00351
00352 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
00353 {
00354 struct ast_var_t *newvariable;
00355 struct varshead *headp;
00356 int x;
00357
00358 for (x = 0; cdr_readonly_vars[x]; x++) {
00359 if (!strcasecmp(name, cdr_readonly_vars[x])) {
00360 ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
00361 return -1;
00362 }
00363 }
00364
00365 if (!cdr) {
00366 ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
00367 return -1;
00368 }
00369
00370 for (; cdr; cdr = recur ? cdr->next : NULL) {
00371 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00372 continue;
00373 headp = &cdr->varshead;
00374 AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
00375 if (!strcasecmp(ast_var_name(newvariable), name)) {
00376
00377 AST_LIST_REMOVE_CURRENT(entries);
00378 ast_var_delete(newvariable);
00379 break;
00380 }
00381 }
00382 AST_LIST_TRAVERSE_SAFE_END;
00383
00384 if (value) {
00385 newvariable = ast_var_assign(name, value);
00386 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00387 }
00388 }
00389
00390 return 0;
00391 }
00392
00393 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
00394 {
00395 struct ast_var_t *variables, *newvariable = NULL;
00396 struct varshead *headpa, *headpb;
00397 const char *var, *val;
00398 int x = 0;
00399
00400 if (!to_cdr || !from_cdr)
00401 return 0;
00402
00403 headpa = &from_cdr->varshead;
00404 headpb = &to_cdr->varshead;
00405
00406 AST_LIST_TRAVERSE(headpa,variables,entries) {
00407 if (variables &&
00408 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00409 !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00410 newvariable = ast_var_assign(var, val);
00411 AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
00412 x++;
00413 }
00414 }
00415
00416 return x;
00417 }
00418
00419 int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
00420 {
00421 struct ast_var_t *variables;
00422 const char *var;
00423 char *tmp;
00424 char workspace[256];
00425 int total = 0, x = 0, i;
00426
00427 ast_str_reset(*buf);
00428
00429 for (; cdr; cdr = recur ? cdr->next : NULL) {
00430 if (++x > 1)
00431 ast_str_append(buf, 0, "\n");
00432
00433 AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
00434 if (!(var = ast_var_name(variables))) {
00435 continue;
00436 }
00437
00438 if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, S_OR(ast_var_value(variables), ""), sep) < 0) {
00439 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00440 break;
00441 }
00442
00443 total++;
00444 }
00445
00446 for (i = 0; cdr_readonly_vars[i]; i++) {
00447 workspace[0] = 0;
00448 ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
00449 if (!tmp)
00450 continue;
00451
00452 if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
00453 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00454 break;
00455 } else
00456 total++;
00457 }
00458 }
00459
00460 return total;
00461 }
00462
00463
00464 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
00465 {
00466
00467
00468 for (; cdr; cdr = recur ? cdr->next : NULL) {
00469 struct ast_var_t *vardata;
00470 struct varshead *headp = &cdr->varshead;
00471 while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
00472 ast_var_delete(vardata);
00473 }
00474 }
00475
00476
00477 static void check_post(struct ast_cdr *cdr)
00478 {
00479 if (!cdr)
00480 return;
00481 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00482 ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
00483 }
00484
00485 void ast_cdr_free(struct ast_cdr *cdr)
00486 {
00487
00488 while (cdr) {
00489 struct ast_cdr *next = cdr->next;
00490
00491 ast_cdr_free_vars(cdr, 0);
00492 ast_free(cdr);
00493 cdr = next;
00494 }
00495 }
00496
00497
00498 void ast_cdr_discard(struct ast_cdr *cdr)
00499 {
00500 while (cdr) {
00501 struct ast_cdr *next = cdr->next;
00502
00503 ast_cdr_free_vars(cdr, 0);
00504 ast_free(cdr);
00505 cdr = next;
00506 }
00507 }
00508
00509 struct ast_cdr *ast_cdr_alloc(void)
00510 {
00511 struct ast_cdr *x;
00512 x = ast_calloc(1, sizeof(*x));
00513 if (!x)
00514 ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
00515 return x;
00516 }
00517
00518 static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
00519 {
00520 struct ast_var_t *variablesfrom,*variablesto;
00521 struct varshead *headpfrom = &to->varshead;
00522 struct varshead *headpto = &from->varshead;
00523 AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
00524
00525 const char *fromvarname, *fromvarval;
00526 const char *tovarname = NULL, *tovarval = NULL;
00527 fromvarname = ast_var_name(variablesfrom);
00528 fromvarval = ast_var_value(variablesfrom);
00529 tovarname = 0;
00530
00531
00532 AST_LIST_TRAVERSE(headpto, variablesto, entries) {
00533
00534
00535 if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
00536 tovarname = ast_var_name(variablesto);
00537 tovarval = ast_var_value(variablesto);
00538 break;
00539 }
00540 }
00541 if (tovarname && strcasecmp(fromvarval,tovarval) != 0) {
00542 ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
00543 continue;
00544 } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0)
00545 continue;
00546
00547
00548 AST_LIST_MOVE_CURRENT(headpto, entries);
00549 }
00550 AST_LIST_TRAVERSE_SAFE_END;
00551 }
00552
00553 void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
00554 {
00555 struct ast_cdr *zcdr;
00556 struct ast_cdr *lto = NULL;
00557 struct ast_cdr *lfrom = NULL;
00558 int discard_from = 0;
00559
00560 if (!to || !from)
00561 return;
00562
00563
00564 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00565 zcdr = to;
00566 while (to->next) {
00567 lto = to;
00568 to = to->next;
00569 }
00570
00571 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00572 ast_log(LOG_WARNING, "Merging into locked CDR... no choice.\n");
00573 to = zcdr;
00574 lto = NULL;
00575 }
00576 }
00577
00578 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
00579 struct ast_cdr *llfrom = NULL;
00580 discard_from = 1;
00581 if (lto) {
00582
00583 lto->next = from;
00584 lfrom = from;
00585 while (lfrom && lfrom->next) {
00586 if (!lfrom->next->next)
00587 llfrom = lfrom;
00588 lfrom = lfrom->next;
00589 }
00590
00591 if (llfrom) {
00592 llfrom->next = to;
00593 }
00594 from = lfrom;
00595 } else {
00596
00597 struct ast_cdr tcdr;
00598 memcpy(&tcdr, to, sizeof(tcdr));
00599
00600 memcpy(to, from, sizeof(*to));
00601 lfrom = from;
00602 while (lfrom && lfrom->next) {
00603 if (!lfrom->next->next)
00604 llfrom = lfrom;
00605 lfrom = lfrom->next;
00606 }
00607 from->next = NULL;
00608
00609 if (llfrom == from) {
00610 to = to->next = ast_cdr_dup(&tcdr);
00611 } else if (llfrom) {
00612 to = llfrom->next = ast_cdr_dup(&tcdr);
00613 }
00614 from = lfrom;
00615 }
00616 }
00617
00618 if (!ast_tvzero(from->start)) {
00619 if (!ast_tvzero(to->start)) {
00620 if (ast_tvcmp(to->start, from->start) > 0 ) {
00621 to->start = from->start;
00622 from->start = ast_tv(0,0);
00623 }
00624
00625 } else {
00626 to->start = from->start;
00627 from->start = ast_tv(0,0);
00628 }
00629 }
00630 if (!ast_tvzero(from->answer)) {
00631 if (!ast_tvzero(to->answer)) {
00632 if (ast_tvcmp(to->answer, from->answer) > 0 ) {
00633 to->answer = from->answer;
00634 from->answer = ast_tv(0,0);
00635 }
00636
00637 } else {
00638 to->answer = from->answer;
00639 from->answer = ast_tv(0,0);
00640 }
00641 }
00642 if (!ast_tvzero(from->end)) {
00643 if (!ast_tvzero(to->end)) {
00644 if (ast_tvcmp(to->end, from->end) < 0 ) {
00645 to->end = from->end;
00646 from->end = ast_tv(0,0);
00647 to->duration = to->end.tv_sec - to->start.tv_sec;
00648 to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00649 }
00650
00651 } else {
00652 to->end = from->end;
00653 from->end = ast_tv(0,0);
00654 to->duration = to->end.tv_sec - to->start.tv_sec;
00655 to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00656 }
00657 }
00658 if (to->disposition < from->disposition) {
00659 to->disposition = from->disposition;
00660 from->disposition = AST_CDR_NOANSWER;
00661 }
00662 if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
00663 ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
00664 from->lastapp[0] = 0;
00665 }
00666 if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
00667 ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
00668 from->lastdata[0] = 0;
00669 }
00670 if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
00671 ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
00672 from->dcontext[0] = 0;
00673 }
00674 if (ast_strlen_zero(to->dstchannel) && !ast_strlen_zero(from->dstchannel)) {
00675 ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
00676 from->dstchannel[0] = 0;
00677 }
00678 if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
00679 ast_copy_string(to->channel, from->channel, sizeof(to->channel));
00680 from->channel[0] = 0;
00681 }
00682 if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
00683 ast_copy_string(to->src, from->src, sizeof(to->src));
00684 from->src[0] = 0;
00685 }
00686 if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
00687 ast_copy_string(to->clid, from->clid, sizeof(to->clid));
00688 from->clid[0] = 0;
00689 }
00690 if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
00691 ast_copy_string(to->dst, from->dst, sizeof(to->dst));
00692 from->dst[0] = 0;
00693 }
00694 if (!to->amaflags)
00695 to->amaflags = AST_CDR_DOCUMENTATION;
00696 if (!from->amaflags)
00697 from->amaflags = AST_CDR_DOCUMENTATION;
00698 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (to->amaflags == AST_CDR_DOCUMENTATION && from->amaflags != AST_CDR_DOCUMENTATION)) {
00699 to->amaflags = from->amaflags;
00700 }
00701 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
00702 ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
00703 }
00704 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->peeraccount) && !ast_strlen_zero(from->peeraccount))) {
00705 ast_copy_string(to->peeraccount, from->peeraccount, sizeof(to->peeraccount));
00706 }
00707 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
00708 ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
00709 }
00710
00711 cdr_merge_vars(from, to);
00712
00713 if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
00714 ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
00715 if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
00716 ast_set_flag(to, AST_CDR_FLAG_POSTED);
00717 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
00718 ast_set_flag(to, AST_CDR_FLAG_LOCKED);
00719 if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
00720 ast_set_flag(to, AST_CDR_FLAG_CHILD);
00721 if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
00722 ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
00723
00724
00725 while (from->next) {
00726
00727 zcdr = from->next;
00728 from->next = zcdr->next;
00729 zcdr->next = NULL;
00730
00731 ast_cdr_append(to, zcdr);
00732 }
00733 if (discard_from)
00734 ast_cdr_discard(from);
00735 }
00736
00737 void ast_cdr_start(struct ast_cdr *cdr)
00738 {
00739 for (; cdr; cdr = cdr->next) {
00740 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00741 check_post(cdr);
00742 cdr->start = ast_tvnow();
00743 }
00744 }
00745 }
00746
00747 void ast_cdr_answer(struct ast_cdr *cdr)
00748 {
00749
00750 for (; cdr; cdr = cdr->next) {
00751 if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00752 continue;
00753 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00754 continue;
00755 check_post(cdr);
00756 if (cdr->disposition < AST_CDR_ANSWERED)
00757 cdr->disposition = AST_CDR_ANSWERED;
00758 if (ast_tvzero(cdr->answer))
00759 cdr->answer = ast_tvnow();
00760 }
00761 }
00762
00763 void ast_cdr_busy(struct ast_cdr *cdr)
00764 {
00765
00766 for (; cdr; cdr = cdr->next) {
00767 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00768 check_post(cdr);
00769 cdr->disposition = AST_CDR_BUSY;
00770 }
00771 }
00772 }
00773
00774 void ast_cdr_failed(struct ast_cdr *cdr)
00775 {
00776 for (; cdr; cdr = cdr->next) {
00777 check_post(cdr);
00778 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00779 check_post(cdr);
00780 if (cdr->disposition < AST_CDR_FAILED)
00781 cdr->disposition = AST_CDR_FAILED;
00782 }
00783 }
00784 }
00785
00786 void ast_cdr_noanswer(struct ast_cdr *cdr)
00787 {
00788 while (cdr) {
00789 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00790 check_post(cdr);
00791 cdr->disposition = AST_CDR_NOANSWER;
00792 }
00793 cdr = cdr->next;
00794 }
00795 }
00796
00797 void ast_cdr_congestion(struct ast_cdr *cdr)
00798 {
00799 char *chan;
00800
00801
00802 if (!congestion) {
00803 ast_cdr_failed(cdr);
00804 }
00805
00806 while (cdr && congestion) {
00807 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00808 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00809
00810 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED)) {
00811 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00812 }
00813
00814 if (cdr->disposition < AST_CDR_CONGESTION) {
00815 cdr->disposition = AST_CDR_CONGESTION;
00816 }
00817 }
00818 cdr = cdr->next;
00819 }
00820 }
00821
00822
00823
00824
00825 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00826 {
00827 int res = 0;
00828
00829 for (; cdr; cdr = cdr->next) {
00830 switch (cause) {
00831
00832 case AST_CAUSE_BUSY:
00833 ast_cdr_busy(cdr);
00834 break;
00835 case AST_CAUSE_NO_ANSWER:
00836 ast_cdr_noanswer(cdr);
00837 break;
00838 case AST_CAUSE_NORMAL_CIRCUIT_CONGESTION:
00839 ast_cdr_congestion(cdr);
00840 break;
00841 case AST_CAUSE_NORMAL:
00842 break;
00843 default:
00844 res = -1;
00845 }
00846 }
00847 return res;
00848 }
00849
00850 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
00851 {
00852 for (; cdr; cdr = cdr->next) {
00853 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00854 check_post(cdr);
00855 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00856 }
00857 }
00858 }
00859
00860 void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data)
00861 {
00862
00863 for (; cdr; cdr = cdr->next) {
00864 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00865 check_post(cdr);
00866 ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
00867 ast_copy_string(cdr->lastdata, S_OR(data, ""), sizeof(cdr->lastdata));
00868 }
00869 }
00870 }
00871
00872 void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t)
00873 {
00874
00875 for (; cdr; cdr = cdr->next) {
00876 if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00877 continue;
00878 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00879 continue;
00880 check_post(cdr);
00881 cdr->answer = t;
00882 }
00883 }
00884
00885 void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition)
00886 {
00887
00888 for (; cdr; cdr = cdr->next) {
00889 if (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00890 continue;
00891 check_post(cdr);
00892 cdr->disposition = disposition;
00893 }
00894 }
00895
00896
00897 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
00898 {
00899 const char *num;
00900
00901 if (!cdr) {
00902 return;
00903 }
00904
00905
00906 num = S_COR(ast_channel_caller(c)->ani.number.valid, ast_channel_caller(c)->ani.number.str,
00907 S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, NULL));
00908 ast_callerid_merge(cdr->clid, sizeof(cdr->clid),
00909 S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, NULL), num, "");
00910 ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
00911 ast_cdr_setvar(cdr, "dnid", S_OR(ast_channel_dialed(c)->number.str, ""), 0);
00912
00913 if (ast_channel_caller(c)->id.subaddress.valid) {
00914 ast_cdr_setvar(cdr, "callingsubaddr", S_OR(ast_channel_caller(c)->id.subaddress.str, ""), 0);
00915 }
00916 if (ast_channel_dialed(c)->subaddress.valid) {
00917 ast_cdr_setvar(cdr, "calledsubaddr", S_OR(ast_channel_dialed(c)->subaddress.str, ""), 0);
00918 }
00919 }
00920
00921 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00922 {
00923 for (; cdr; cdr = cdr->next) {
00924 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00925 set_one_cid(cdr, c);
00926 }
00927 return 0;
00928 }
00929
00930 static int cdr_seq_inc(struct ast_cdr *cdr)
00931 {
00932 return (cdr->sequence = ast_atomic_fetchadd_int(&cdr_sequence, +1));
00933 }
00934
00935 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00936 {
00937 for ( ; cdr ; cdr = cdr->next) {
00938 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00939 ast_copy_string(cdr->channel, ast_channel_name(c), sizeof(cdr->channel));
00940 set_one_cid(cdr, c);
00941 cdr_seq_inc(cdr);
00942
00943 cdr->disposition = (ast_channel_state(c) == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER;
00944 cdr->amaflags = ast_channel_amaflags(c) ? ast_channel_amaflags(c) : ast_default_amaflags;
00945 ast_copy_string(cdr->accountcode, ast_channel_accountcode(c), sizeof(cdr->accountcode));
00946 ast_copy_string(cdr->peeraccount, ast_channel_peeraccount(c), sizeof(cdr->peeraccount));
00947
00948 ast_copy_string(cdr->dst, S_OR(ast_channel_macroexten(c),ast_channel_exten(c)), sizeof(cdr->dst));
00949 ast_copy_string(cdr->dcontext, S_OR(ast_channel_macrocontext(c),ast_channel_context(c)), sizeof(cdr->dcontext));
00950
00951 ast_copy_string(cdr->uniqueid, ast_channel_uniqueid(c), sizeof(cdr->uniqueid));
00952
00953 ast_copy_string(cdr->linkedid, ast_channel_linkedid(c), sizeof(cdr->linkedid));
00954 }
00955 }
00956 return 0;
00957 }
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971 void ast_cdr_end(struct ast_cdr *cdr)
00972 {
00973 for ( ; cdr ; cdr = cdr->next) {
00974 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00975 continue;
00976 check_post(cdr);
00977 if (ast_tvzero(cdr->end))
00978 cdr->end = ast_tvnow();
00979 if (ast_tvzero(cdr->start)) {
00980 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00981 cdr->disposition = AST_CDR_FAILED;
00982 } else
00983 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00984 if (ast_tvzero(cdr->answer)) {
00985 if (cdr->disposition == AST_CDR_ANSWERED) {
00986 ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
00987 cdr->disposition = AST_CDR_FAILED;
00988 }
00989 } else {
00990 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
00991 if (ast_test_flag(&ast_options, AST_OPT_FLAG_INITIATED_SECONDS))
00992 cdr->billsec += cdr->end.tv_usec > cdr->answer.tv_usec ? 1 : 0;
00993 }
00994 }
00995 }
00996
00997 char *ast_cdr_disp2str(int disposition)
00998 {
00999 switch (disposition) {
01000 case AST_CDR_NULL:
01001 return "NO ANSWER";
01002 case AST_CDR_NOANSWER:
01003 return "NO ANSWER";
01004 case AST_CDR_FAILED:
01005 return "FAILED";
01006 case AST_CDR_BUSY:
01007 return "BUSY";
01008 case AST_CDR_ANSWERED:
01009 return "ANSWERED";
01010 case AST_CDR_CONGESTION:
01011 return "CONGESTION";
01012 }
01013 return "UNKNOWN";
01014 }
01015
01016
01017
01018
01019
01020 char *ast_cdr_flags2str(int flag)
01021 {
01022 switch (flag) {
01023 case AST_CDR_OMIT:
01024 return "OMIT";
01025 case AST_CDR_BILLING:
01026 return "BILLING";
01027 case AST_CDR_DOCUMENTATION:
01028 return "DOCUMENTATION";
01029 }
01030 return "Unknown";
01031 }
01032
01033 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
01034 {
01035 struct ast_cdr *cdr = ast_channel_cdr(chan);
01036 const char *old_acct = "";
01037
01038 if (!ast_strlen_zero(ast_channel_accountcode(chan))) {
01039 old_acct = ast_strdupa(ast_channel_accountcode(chan));
01040 }
01041
01042 ast_channel_accountcode_set(chan, account);
01043 for ( ; cdr ; cdr = cdr->next) {
01044 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01045 ast_copy_string(cdr->accountcode, ast_channel_accountcode(chan), sizeof(cdr->accountcode));
01046 }
01047 }
01048
01049
01050
01051
01052
01053 ast_manager_event(chan, EVENT_FLAG_CALL, "NewAccountCode",
01054 "Channel: %s\r\n"
01055 "Uniqueid: %s\r\n"
01056 "AccountCode: %s\r\n"
01057 "OldAccountCode: %s\r\n",
01058 ast_channel_name(chan), ast_channel_uniqueid(chan), ast_channel_accountcode(chan), old_acct);
01059
01060 return 0;
01061 }
01062
01063 int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
01064 {
01065 struct ast_cdr *cdr = ast_channel_cdr(chan);
01066 const char *old_acct = "";
01067
01068 if (!ast_strlen_zero(ast_channel_peeraccount(chan))) {
01069 old_acct = ast_strdupa(ast_channel_peeraccount(chan));
01070 }
01071
01072 ast_channel_peeraccount_set(chan, account);
01073 for ( ; cdr ; cdr = cdr->next) {
01074 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01075 ast_copy_string(cdr->peeraccount, ast_channel_peeraccount(chan), sizeof(cdr->peeraccount));
01076 }
01077 }
01078
01079
01080
01081
01082
01083 ast_manager_event(chan, EVENT_FLAG_CALL, "NewPeerAccount",
01084 "Channel: %s\r\n"
01085 "Uniqueid: %s\r\n"
01086 "PeerAccount: %s\r\n"
01087 "OldPeerAccount: %s\r\n",
01088 ast_channel_name(chan), ast_channel_uniqueid(chan), ast_channel_peeraccount(chan), old_acct);
01089
01090 return 0;
01091 }
01092
01093 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
01094 {
01095 struct ast_cdr *cdr;
01096 int newflag = ast_cdr_amaflags2int(flag);
01097 if (newflag) {
01098 for (cdr = ast_channel_cdr(chan); cdr; cdr = cdr->next) {
01099 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01100 cdr->amaflags = newflag;
01101 }
01102 }
01103 }
01104
01105 return 0;
01106 }
01107
01108 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
01109 {
01110 struct ast_cdr *cdr = ast_channel_cdr(chan);
01111
01112 for ( ; cdr ; cdr = cdr->next) {
01113 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
01114 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
01115 }
01116
01117 return 0;
01118 }
01119
01120 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
01121 {
01122 struct ast_cdr *cdr = ast_channel_cdr(chan);
01123
01124 for ( ; cdr ; cdr = cdr->next) {
01125 int len = strlen(cdr->userfield);
01126
01127 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
01128 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
01129 }
01130
01131 return 0;
01132 }
01133
01134 int ast_cdr_update(struct ast_channel *c)
01135 {
01136 struct ast_cdr *cdr = ast_channel_cdr(c);
01137
01138 for ( ; cdr ; cdr = cdr->next) {
01139 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01140 set_one_cid(cdr, c);
01141
01142
01143 ast_copy_string(cdr->accountcode, ast_channel_accountcode(c), sizeof(cdr->accountcode));
01144 ast_copy_string(cdr->peeraccount, ast_channel_peeraccount(c), sizeof(cdr->peeraccount));
01145 ast_copy_string(cdr->linkedid, ast_channel_linkedid(c), sizeof(cdr->linkedid));
01146
01147
01148 ast_copy_string(cdr->dst, S_OR(ast_channel_macroexten(c), ast_channel_exten(c)), sizeof(cdr->dst));
01149 ast_copy_string(cdr->dcontext, S_OR(ast_channel_macrocontext(c), ast_channel_context(c)), sizeof(cdr->dcontext));
01150 }
01151 }
01152
01153 return 0;
01154 }
01155
01156 int ast_cdr_amaflags2int(const char *flag)
01157 {
01158 if (!strcasecmp(flag, "default"))
01159 return 0;
01160 if (!strcasecmp(flag, "omit"))
01161 return AST_CDR_OMIT;
01162 if (!strcasecmp(flag, "billing"))
01163 return AST_CDR_BILLING;
01164 if (!strcasecmp(flag, "documentation"))
01165 return AST_CDR_DOCUMENTATION;
01166 return -1;
01167 }
01168
01169 static void post_cdr(struct ast_cdr *cdr)
01170 {
01171 struct ast_cdr_beitem *i;
01172
01173 for ( ; cdr ; cdr = cdr->next) {
01174 if (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
01175
01176 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01177 continue;
01178 }
01179
01180
01181
01182
01183 if (ast_test_flag(cdr, AST_CDR_FLAG_DIALED) && !ast_test_flag(cdr, AST_CDR_FLAG_ORIGINATED)) {
01184 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01185 continue;
01186 }
01187
01188 check_post(cdr);
01189 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01190 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01191 continue;
01192 AST_RWLIST_RDLOCK(&be_list);
01193 AST_RWLIST_TRAVERSE(&be_list, i, list) {
01194 i->be(cdr);
01195 }
01196 AST_RWLIST_UNLOCK(&be_list);
01197 }
01198 }
01199
01200 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01201 {
01202 struct ast_cdr *duplicate;
01203 struct ast_flags flags = { 0 };
01204
01205 if (_flags)
01206 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01207
01208 for ( ; cdr ; cdr = cdr->next) {
01209
01210 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01211 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01212 ast_cdr_end(cdr);
01213 if ((duplicate = ast_cdr_dup_unique_swap(cdr))) {
01214 ast_cdr_detach(duplicate);
01215 }
01216 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01217 }
01218
01219
01220 if (ast_test_flag(&flags, AST_CDR_FLAG_POST_ENABLE)) {
01221 ast_clear_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01222 continue;
01223 }
01224
01225
01226 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01227 ast_cdr_free_vars(cdr, 0);
01228 }
01229
01230
01231 ast_clear_flag(cdr, AST_FLAGS_ALL);
01232 memset(&cdr->start, 0, sizeof(cdr->start));
01233 memset(&cdr->end, 0, sizeof(cdr->end));
01234 memset(&cdr->answer, 0, sizeof(cdr->answer));
01235 cdr->billsec = 0;
01236 cdr->duration = 0;
01237 ast_cdr_start(cdr);
01238 cdr->disposition = AST_CDR_NOANSWER;
01239 }
01240 }
01241 }
01242
01243 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01244 {
01245 struct ast_flags flags = { 0 };
01246
01247 if (_flags)
01248 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01249
01250
01251 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) {
01252 ast_clear_flag(cdr, AST_FLAGS_ALL);
01253 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01254 } else {
01255 ast_clear_flag(cdr, AST_FLAGS_ALL);
01256 }
01257
01258 memset(&cdr->start, 0, sizeof(cdr->start));
01259 memset(&cdr->end, 0, sizeof(cdr->end));
01260 memset(&cdr->answer, 0, sizeof(cdr->answer));
01261 cdr->billsec = 0;
01262 cdr->duration = 0;
01263 ast_cdr_start(cdr);
01264 cdr->disposition = AST_CDR_NULL;
01265 }
01266
01267 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01268 {
01269 struct ast_cdr *ret;
01270
01271 if (cdr) {
01272 ret = cdr;
01273
01274 while (cdr->next)
01275 cdr = cdr->next;
01276 cdr->next = newcdr;
01277 } else {
01278 ret = newcdr;
01279 }
01280
01281 return ret;
01282 }
01283
01284
01285 static void reset_batch(void)
01286 {
01287 batch->size = 0;
01288 batch->head = NULL;
01289 batch->tail = NULL;
01290 }
01291
01292
01293 static int init_batch(void)
01294 {
01295
01296 if (!(batch = ast_malloc(sizeof(*batch))))
01297 return -1;
01298
01299 reset_batch();
01300
01301 return 0;
01302 }
01303
01304 static void *do_batch_backend_process(void *data)
01305 {
01306 struct ast_cdr_batch_item *processeditem;
01307 struct ast_cdr_batch_item *batchitem = data;
01308
01309
01310 while (batchitem) {
01311 post_cdr(batchitem->cdr);
01312 ast_cdr_free(batchitem->cdr);
01313 processeditem = batchitem;
01314 batchitem = batchitem->next;
01315 ast_free(processeditem);
01316 }
01317
01318 return NULL;
01319 }
01320
01321 void ast_cdr_submit_batch(int do_shutdown)
01322 {
01323 struct ast_cdr_batch_item *oldbatchitems = NULL;
01324 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01325
01326
01327 if (!batch || !batch->head)
01328 return;
01329
01330
01331 ast_mutex_lock(&cdr_batch_lock);
01332 oldbatchitems = batch->head;
01333 reset_batch();
01334 ast_mutex_unlock(&cdr_batch_lock);
01335
01336
01337
01338 if (batchscheduleronly || do_shutdown) {
01339 ast_debug(1, "CDR single-threaded batch processing begins now\n");
01340 do_batch_backend_process(oldbatchitems);
01341 } else {
01342 if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
01343 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01344 do_batch_backend_process(oldbatchitems);
01345 } else {
01346 ast_debug(1, "CDR multi-threaded batch processing begins now\n");
01347 }
01348 }
01349 }
01350
01351 static int submit_scheduled_batch(const void *data)
01352 {
01353 ast_cdr_submit_batch(0);
01354
01355 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01356
01357 return 0;
01358 }
01359
01360 static void submit_unscheduled_batch(void)
01361 {
01362
01363 AST_SCHED_DEL(sched, cdr_sched);
01364
01365 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01366
01367 ast_mutex_lock(&cdr_pending_lock);
01368 ast_cond_signal(&cdr_pending_cond);
01369 ast_mutex_unlock(&cdr_pending_lock);
01370 }
01371
01372 void ast_cdr_detach(struct ast_cdr *cdr)
01373 {
01374 struct ast_cdr_batch_item *newtail;
01375 int curr;
01376
01377 if (!cdr)
01378 return;
01379
01380
01381 if (!enabled) {
01382 ast_debug(1, "Dropping CDR !\n");
01383 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01384 ast_cdr_free(cdr);
01385 return;
01386 }
01387
01388
01389 if (!batchmode) {
01390 post_cdr(cdr);
01391 ast_cdr_free(cdr);
01392 return;
01393 }
01394
01395
01396 ast_debug(1, "CDR detaching from this thread\n");
01397
01398
01399 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01400 post_cdr(cdr);
01401 ast_cdr_free(cdr);
01402 return;
01403 }
01404
01405
01406 ast_mutex_lock(&cdr_batch_lock);
01407 if (!batch)
01408 init_batch();
01409 if (!batch->head) {
01410
01411 batch->head = newtail;
01412 } else {
01413
01414 batch->tail->next = newtail;
01415 }
01416 newtail->cdr = cdr;
01417 batch->tail = newtail;
01418 curr = batch->size++;
01419 ast_mutex_unlock(&cdr_batch_lock);
01420
01421
01422 if (curr >= (batchsize - 1))
01423 submit_unscheduled_batch();
01424 }
01425
01426 static void *do_cdr(void *data)
01427 {
01428 struct timespec timeout;
01429 int schedms;
01430 int numevents = 0;
01431
01432 for (;;) {
01433 struct timeval now;
01434 schedms = ast_sched_wait(sched);
01435
01436 if (schedms <= 0)
01437 schedms = 1000;
01438 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01439 timeout.tv_sec = now.tv_sec;
01440 timeout.tv_nsec = now.tv_usec * 1000;
01441
01442 ast_mutex_lock(&cdr_pending_lock);
01443 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01444 numevents = ast_sched_runq(sched);
01445 ast_mutex_unlock(&cdr_pending_lock);
01446 ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01447 }
01448
01449 return NULL;
01450 }
01451
01452 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01453 {
01454 struct ast_cdr_beitem *beitem=NULL;
01455 int cnt=0;
01456 long nextbatchtime=0;
01457
01458 switch (cmd) {
01459 case CLI_INIT:
01460 e->command = "cdr show status";
01461 e->usage =
01462 "Usage: cdr show status\n"
01463 " Displays the Call Detail Record engine system status.\n";
01464 return NULL;
01465 case CLI_GENERATE:
01466 return NULL;
01467 }
01468
01469 if (a->argc > 3)
01470 return CLI_SHOWUSAGE;
01471
01472 ast_cli(a->fd, "\n");
01473 ast_cli(a->fd, "Call Detail Record (CDR) settings\n");
01474 ast_cli(a->fd, "----------------------------------\n");
01475 ast_cli(a->fd, " Logging: %s\n", enabled ? "Enabled" : "Disabled");
01476 ast_cli(a->fd, " Mode: %s\n", batchmode ? "Batch" : "Simple");
01477 if (enabled) {
01478 ast_cli(a->fd, " Log unanswered calls: %s\n", unanswered ? "Yes" : "No");
01479 ast_cli(a->fd, " Log congestion: %s\n\n", congestion ? "Yes" : "No");
01480 if (batchmode) {
01481 ast_cli(a->fd, "* Batch Mode Settings\n");
01482 ast_cli(a->fd, " -------------------\n");
01483 if (batch)
01484 cnt = batch->size;
01485 if (cdr_sched > -1)
01486 nextbatchtime = ast_sched_when(sched, cdr_sched);
01487 ast_cli(a->fd, " Safe shutdown: %s\n", batchsafeshutdown ? "Enabled" : "Disabled");
01488 ast_cli(a->fd, " Threading model: %s\n", batchscheduleronly ? "Scheduler only" : "Scheduler plus separate threads");
01489 ast_cli(a->fd, " Current batch size: %d record%s\n", cnt, ESS(cnt));
01490 ast_cli(a->fd, " Maximum batch size: %d record%s\n", batchsize, ESS(batchsize));
01491 ast_cli(a->fd, " Maximum batch time: %d second%s\n", batchtime, ESS(batchtime));
01492 ast_cli(a->fd, " Next batch processing time: %ld second%s\n\n", nextbatchtime, ESS(nextbatchtime));
01493 }
01494 ast_cli(a->fd, "* Registered Backends\n");
01495 ast_cli(a->fd, " -------------------\n");
01496 AST_RWLIST_RDLOCK(&be_list);
01497 if (AST_RWLIST_EMPTY(&be_list)) {
01498 ast_cli(a->fd, " (none)\n");
01499 } else {
01500 AST_RWLIST_TRAVERSE(&be_list, beitem, list) {
01501 ast_cli(a->fd, " %s\n", beitem->name);
01502 }
01503 }
01504 AST_RWLIST_UNLOCK(&be_list);
01505 ast_cli(a->fd, "\n");
01506 }
01507
01508 return CLI_SUCCESS;
01509 }
01510
01511 static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01512 {
01513 switch (cmd) {
01514 case CLI_INIT:
01515 e->command = "cdr submit";
01516 e->usage =
01517 "Usage: cdr submit\n"
01518 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
01519 return NULL;
01520 case CLI_GENERATE:
01521 return NULL;
01522 }
01523 if (a->argc > 2)
01524 return CLI_SHOWUSAGE;
01525
01526 submit_unscheduled_batch();
01527 ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01528
01529 return CLI_SUCCESS;
01530 }
01531
01532 static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
01533 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
01534
01535 static int do_reload(int reload)
01536 {
01537 struct ast_config *config;
01538 struct ast_variable *v;
01539 int cfg_size;
01540 int cfg_time;
01541 int was_enabled;
01542 int was_batchmode;
01543 int res = 0;
01544 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01545
01546 if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
01547 return 0;
01548 }
01549
01550 ast_mutex_lock(&cdr_batch_lock);
01551
01552 was_enabled = enabled;
01553 was_batchmode = batchmode;
01554
01555 batchsize = BATCH_SIZE_DEFAULT;
01556 batchtime = BATCH_TIME_DEFAULT;
01557 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01558 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01559 enabled = ENABLED_DEFAULT;
01560 batchmode = BATCHMODE_DEFAULT;
01561 unanswered = UNANSWERED_DEFAULT;
01562 congestion = CONGESTION_DEFAULT;
01563
01564 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01565 ast_mutex_unlock(&cdr_batch_lock);
01566 return 0;
01567 }
01568
01569
01570 AST_SCHED_DEL(sched, cdr_sched);
01571
01572 for (v = ast_variable_browse(config, "general"); v; v = v->next) {
01573 if (!strcasecmp(v->name, "enable")) {
01574 enabled = ast_true(v->value);
01575 } else if (!strcasecmp(v->name, "unanswered")) {
01576 unanswered = ast_true(v->value);
01577 } else if (!strcasecmp(v->name, "congestion")) {
01578 congestion = ast_true(v->value);
01579 } else if (!strcasecmp(v->name, "batch")) {
01580 batchmode = ast_true(v->value);
01581 } else if (!strcasecmp(v->name, "scheduleronly")) {
01582 batchscheduleronly = ast_true(v->value);
01583 } else if (!strcasecmp(v->name, "safeshutdown")) {
01584 batchsafeshutdown = ast_true(v->value);
01585 } else if (!strcasecmp(v->name, "size")) {
01586 if (sscanf(v->value, "%30d", &cfg_size) < 1) {
01587 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
01588 } else if (cfg_size < 0) {
01589 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01590 } else {
01591 batchsize = cfg_size;
01592 }
01593 } else if (!strcasecmp(v->name, "time")) {
01594 if (sscanf(v->value, "%30d", &cfg_time) < 1) {
01595 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", v->value);
01596 } else if (cfg_time < 0) {
01597 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01598 } else {
01599 batchtime = cfg_time;
01600 }
01601 } else if (!strcasecmp(v->name, "endbeforehexten")) {
01602 ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01603 } else if (!strcasecmp(v->name, "initiatedseconds")) {
01604 ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INITIATED_SECONDS);
01605 }
01606 }
01607
01608 if (enabled && !batchmode) {
01609 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01610 } else if (enabled && batchmode) {
01611 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01612 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01613 } else {
01614 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01615 }
01616
01617
01618
01619 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01620 ast_cond_init(&cdr_pending_cond, NULL);
01621 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01622 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01623 AST_SCHED_DEL(sched, cdr_sched);
01624 } else {
01625 ast_cli_register(&cli_submit);
01626 ast_register_atexit(ast_cdr_engine_term);
01627 res = 0;
01628 }
01629
01630
01631 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01632
01633 pthread_cancel(cdr_thread);
01634 pthread_kill(cdr_thread, SIGURG);
01635 pthread_join(cdr_thread, NULL);
01636 cdr_thread = AST_PTHREADT_NULL;
01637 ast_cond_destroy(&cdr_pending_cond);
01638 ast_cli_unregister(&cli_submit);
01639 ast_unregister_atexit(ast_cdr_engine_term);
01640 res = 0;
01641
01642
01643 if (!batchmode && was_batchmode) {
01644 ast_cdr_engine_term();
01645 }
01646 } else {
01647 res = 0;
01648 }
01649
01650 ast_mutex_unlock(&cdr_batch_lock);
01651 ast_config_destroy(config);
01652 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
01653
01654 return res;
01655 }
01656
01657 int ast_cdr_engine_init(void)
01658 {
01659 int res;
01660
01661 sched = ast_sched_context_create();
01662 if (!sched) {
01663 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01664 return -1;
01665 }
01666
01667 ast_cli_register(&cli_status);
01668
01669 res = do_reload(0);
01670 if (res) {
01671 ast_mutex_lock(&cdr_batch_lock);
01672 res = init_batch();
01673 ast_mutex_unlock(&cdr_batch_lock);
01674 }
01675
01676 return res;
01677 }
01678
01679
01680
01681 void ast_cdr_engine_term(void)
01682 {
01683 ast_cdr_submit_batch(batchsafeshutdown);
01684 }
01685
01686 int ast_cdr_engine_reload(void)
01687 {
01688 return do_reload(1);
01689 }
01690
01691 int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)
01692 {
01693 struct ast_cdr *tmpcdr;
01694 struct ast_data *level;
01695 struct ast_var_t *variables;
01696 const char *var, *val;
01697 int x = 1, i;
01698 char workspace[256];
01699 char *tmp;
01700
01701 if (!cdr) {
01702 return -1;
01703 }
01704
01705 for (tmpcdr = cdr; tmpcdr; tmpcdr = (recur ? tmpcdr->next : NULL)) {
01706 level = ast_data_add_node(tree, "level");
01707 if (!level) {
01708 continue;
01709 }
01710
01711 ast_data_add_int(level, "level_number", x);
01712
01713 AST_LIST_TRAVERSE(&tmpcdr->varshead, variables, entries) {
01714 if (variables && (var = ast_var_name(variables)) &&
01715 (val = ast_var_value(variables)) && !ast_strlen_zero(var)
01716 && !ast_strlen_zero(val)) {
01717 ast_data_add_str(level, var, val);
01718 } else {
01719 break;
01720 }
01721 }
01722
01723 for (i = 0; cdr_readonly_vars[i]; i++) {
01724 workspace[0] = 0;
01725 ast_cdr_getvar(tmpcdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
01726 if (!tmp) {
01727 continue;
01728 }
01729 ast_data_add_str(level, cdr_readonly_vars[i], tmp);
01730 }
01731
01732 x++;
01733 }
01734
01735 return 0;
01736 }
01737