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