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