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