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