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