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