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: 396279 $")
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 && (newvariable = ast_var_assign(name, value))) {
00376 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00377 }
00378 }
00379
00380 return 0;
00381 }
00382
00383 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
00384 {
00385 struct ast_var_t *variables, *newvariable = NULL;
00386 struct varshead *headpa, *headpb;
00387 const char *var, *val;
00388 int x = 0;
00389
00390 if (!to_cdr || !from_cdr)
00391 return 0;
00392
00393 headpa = &from_cdr->varshead;
00394 headpb = &to_cdr->varshead;
00395
00396 AST_LIST_TRAVERSE(headpa,variables,entries) {
00397 if (variables &&
00398 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00399 !ast_strlen_zero(var) && !ast_strlen_zero(val) &&
00400 (newvariable = ast_var_assign(var, val))) {
00401 AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
00402 x++;
00403 }
00404 }
00405
00406 return x;
00407 }
00408
00409 int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
00410 {
00411 struct ast_var_t *variables;
00412 const char *var;
00413 char *tmp;
00414 char workspace[256];
00415 int total = 0, x = 0, i;
00416
00417 ast_str_reset(*buf);
00418
00419 for (; cdr; cdr = recur ? cdr->next : NULL) {
00420 if (++x > 1)
00421 ast_str_append(buf, 0, "\n");
00422
00423 AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
00424 if (!(var = ast_var_name(variables))) {
00425 continue;
00426 }
00427
00428 if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, S_OR(ast_var_value(variables), ""), sep) < 0) {
00429 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00430 break;
00431 }
00432
00433 total++;
00434 }
00435
00436 for (i = 0; cdr_readonly_vars[i]; i++) {
00437 workspace[0] = 0;
00438 ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
00439 if (!tmp)
00440 continue;
00441
00442 if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
00443 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00444 break;
00445 } else
00446 total++;
00447 }
00448 }
00449
00450 return total;
00451 }
00452
00453
00454 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
00455 {
00456
00457
00458 for (; cdr; cdr = recur ? cdr->next : NULL) {
00459 struct ast_var_t *vardata;
00460 struct varshead *headp = &cdr->varshead;
00461 while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
00462 ast_var_delete(vardata);
00463 }
00464 }
00465
00466
00467 static void check_post(struct ast_cdr *cdr)
00468 {
00469 if (!cdr)
00470 return;
00471 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00472 ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
00473 }
00474
00475 void ast_cdr_free(struct ast_cdr *cdr)
00476 {
00477
00478 while (cdr) {
00479 struct ast_cdr *next = cdr->next;
00480
00481 ast_cdr_free_vars(cdr, 0);
00482 ast_free(cdr);
00483 cdr = next;
00484 }
00485 }
00486
00487
00488 void ast_cdr_discard(struct ast_cdr *cdr)
00489 {
00490 while (cdr) {
00491 struct ast_cdr *next = cdr->next;
00492
00493 ast_cdr_free_vars(cdr, 0);
00494 ast_free(cdr);
00495 cdr = next;
00496 }
00497 }
00498
00499 struct ast_cdr *ast_cdr_alloc(void)
00500 {
00501 struct ast_cdr *x;
00502 x = ast_calloc(1, sizeof(*x));
00503 if (!x)
00504 ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
00505 return x;
00506 }
00507
00508 static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
00509 {
00510 struct ast_var_t *variablesfrom,*variablesto;
00511 struct varshead *headpfrom = &to->varshead;
00512 struct varshead *headpto = &from->varshead;
00513 AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
00514
00515 const char *fromvarname, *fromvarval;
00516 const char *tovarname = NULL, *tovarval = NULL;
00517 fromvarname = ast_var_name(variablesfrom);
00518 fromvarval = ast_var_value(variablesfrom);
00519 tovarname = 0;
00520
00521
00522 AST_LIST_TRAVERSE(headpto, variablesto, entries) {
00523
00524
00525 if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
00526 tovarname = ast_var_name(variablesto);
00527 tovarval = ast_var_value(variablesto);
00528 break;
00529 }
00530 }
00531 if (tovarname && strcasecmp(fromvarval,tovarval) != 0) {
00532 ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
00533 continue;
00534 } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0)
00535 continue;
00536
00537
00538 AST_LIST_MOVE_CURRENT(headpto, entries);
00539 }
00540 AST_LIST_TRAVERSE_SAFE_END;
00541 }
00542
00543 void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
00544 {
00545 struct ast_cdr *zcdr;
00546 struct ast_cdr *lto = NULL;
00547 struct ast_cdr *lfrom = NULL;
00548 int discard_from = 0;
00549
00550 if (!to || !from)
00551 return;
00552
00553
00554 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00555 zcdr = to;
00556 while (to->next) {
00557 lto = to;
00558 to = to->next;
00559 }
00560
00561 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00562 ast_log(LOG_WARNING, "Merging into locked CDR... no choice.\n");
00563 to = zcdr;
00564 lto = NULL;
00565 }
00566 }
00567
00568 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
00569 struct ast_cdr *llfrom = NULL;
00570 discard_from = 1;
00571 if (lto) {
00572
00573 lto->next = from;
00574 lfrom = from;
00575 while (lfrom && lfrom->next) {
00576 if (!lfrom->next->next)
00577 llfrom = lfrom;
00578 lfrom = lfrom->next;
00579 }
00580
00581 if (llfrom) {
00582 llfrom->next = to;
00583 }
00584 from = lfrom;
00585 } else {
00586
00587 struct ast_cdr tcdr;
00588 memcpy(&tcdr, to, sizeof(tcdr));
00589
00590 memcpy(to, from, sizeof(*to));
00591 lfrom = from;
00592 while (lfrom && lfrom->next) {
00593 if (!lfrom->next->next)
00594 llfrom = lfrom;
00595 lfrom = lfrom->next;
00596 }
00597 from->next = NULL;
00598
00599 if (llfrom == from) {
00600 to = to->next = ast_cdr_dup(&tcdr);
00601 } else if (llfrom) {
00602 to = llfrom->next = ast_cdr_dup(&tcdr);
00603 }
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
00788
00789
00790 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00791 {
00792 int res = 0;
00793
00794 for (; cdr; cdr = cdr->next) {
00795 switch (cause) {
00796
00797 case AST_CAUSE_BUSY:
00798 ast_cdr_busy(cdr);
00799 break;
00800 case AST_CAUSE_NO_ANSWER:
00801 ast_cdr_noanswer(cdr);
00802 break;
00803 case AST_CAUSE_NORMAL:
00804 break;
00805 default:
00806 res = -1;
00807 }
00808 }
00809 return res;
00810 }
00811
00812 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
00813 {
00814 for (; cdr; cdr = cdr->next) {
00815 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00816 check_post(cdr);
00817 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00818 }
00819 }
00820 }
00821
00822 void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data)
00823 {
00824
00825 for (; cdr; cdr = cdr->next) {
00826 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00827 check_post(cdr);
00828 ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
00829 ast_copy_string(cdr->lastdata, S_OR(data, ""), sizeof(cdr->lastdata));
00830 }
00831 }
00832 }
00833
00834 void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t)
00835 {
00836
00837 for (; cdr; cdr = cdr->next) {
00838 if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00839 continue;
00840 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00841 continue;
00842 check_post(cdr);
00843 cdr->answer = t;
00844 }
00845 }
00846
00847 void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition)
00848 {
00849
00850 for (; cdr; cdr = cdr->next) {
00851 if (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00852 continue;
00853 check_post(cdr);
00854 cdr->disposition = disposition;
00855 }
00856 }
00857
00858
00859 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
00860 {
00861 const char *num;
00862
00863 if (!cdr) {
00864 return;
00865 }
00866
00867
00868 num = S_COR(c->caller.ani.number.valid, c->caller.ani.number.str,
00869 S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL));
00870 ast_callerid_merge(cdr->clid, sizeof(cdr->clid),
00871 S_COR(c->caller.id.name.valid, c->caller.id.name.str, NULL), num, "");
00872 ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
00873 ast_cdr_setvar(cdr, "dnid", S_OR(c->dialed.number.str, ""), 0);
00874
00875 if (c->caller.id.subaddress.valid) {
00876 ast_cdr_setvar(cdr, "callingsubaddr", S_OR(c->caller.id.subaddress.str, ""), 0);
00877 }
00878 if (c->dialed.subaddress.valid) {
00879 ast_cdr_setvar(cdr, "calledsubaddr", S_OR(c->dialed.subaddress.str, ""), 0);
00880 }
00881 }
00882
00883 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00884 {
00885 for (; cdr; cdr = cdr->next) {
00886 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00887 set_one_cid(cdr, c);
00888 }
00889 return 0;
00890 }
00891
00892 static int cdr_seq_inc(struct ast_cdr *cdr)
00893 {
00894 return (cdr->sequence = ast_atomic_fetchadd_int(&cdr_sequence, +1));
00895 }
00896
00897 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00898 {
00899 for ( ; cdr ; cdr = cdr->next) {
00900 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00901 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00902 set_one_cid(cdr, c);
00903 cdr_seq_inc(cdr);
00904
00905 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER;
00906 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
00907 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00908 ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
00909
00910 ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
00911 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
00912
00913 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00914
00915 ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
00916 }
00917 }
00918 return 0;
00919 }
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933 void ast_cdr_end(struct ast_cdr *cdr)
00934 {
00935 for ( ; cdr ; cdr = cdr->next) {
00936 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00937 continue;
00938 check_post(cdr);
00939 if (ast_tvzero(cdr->end))
00940 cdr->end = ast_tvnow();
00941 if (ast_tvzero(cdr->start)) {
00942 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00943 cdr->disposition = AST_CDR_FAILED;
00944 } else
00945 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00946 if (ast_tvzero(cdr->answer)) {
00947 if (cdr->disposition == AST_CDR_ANSWERED) {
00948 ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
00949 cdr->disposition = AST_CDR_FAILED;
00950 }
00951 } else {
00952 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
00953 if (ast_test_flag(&ast_options, AST_OPT_FLAG_INITIATED_SECONDS))
00954 cdr->billsec += cdr->end.tv_usec > cdr->answer.tv_usec ? 1 : 0;
00955 }
00956 }
00957 }
00958
00959 char *ast_cdr_disp2str(int disposition)
00960 {
00961 switch (disposition) {
00962 case AST_CDR_NULL:
00963 return "NO ANSWER";
00964 case AST_CDR_NOANSWER:
00965 return "NO ANSWER";
00966 case AST_CDR_FAILED:
00967 return "FAILED";
00968 case AST_CDR_BUSY:
00969 return "BUSY";
00970 case AST_CDR_ANSWERED:
00971 return "ANSWERED";
00972 }
00973 return "UNKNOWN";
00974 }
00975
00976
00977 char *ast_cdr_flags2str(int flag)
00978 {
00979 switch (flag) {
00980 case AST_CDR_OMIT:
00981 return "OMIT";
00982 case AST_CDR_BILLING:
00983 return "BILLING";
00984 case AST_CDR_DOCUMENTATION:
00985 return "DOCUMENTATION";
00986 }
00987 return "Unknown";
00988 }
00989
00990 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00991 {
00992 struct ast_cdr *cdr = chan->cdr;
00993 const char *old_acct = "";
00994
00995 if (!ast_strlen_zero(chan->accountcode)) {
00996 old_acct = ast_strdupa(chan->accountcode);
00997 }
00998
00999 ast_string_field_set(chan, accountcode, account);
01000 for ( ; cdr ; cdr = cdr->next) {
01001 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01002 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
01003 }
01004 }
01005
01006 ast_manager_event(chan, EVENT_FLAG_CALL, "NewAccountCode",
01007 "Channel: %s\r\n"
01008 "Uniqueid: %s\r\n"
01009 "AccountCode: %s\r\n"
01010 "OldAccountCode: %s\r\n",
01011 chan->name, chan->uniqueid, chan->accountcode, old_acct);
01012
01013 return 0;
01014 }
01015
01016 int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
01017 {
01018 struct ast_cdr *cdr = chan->cdr;
01019 const char *old_acct = "";
01020
01021 if (!ast_strlen_zero(chan->peeraccount)) {
01022 old_acct = ast_strdupa(chan->peeraccount);
01023 }
01024
01025 ast_string_field_set(chan, peeraccount, account);
01026 for ( ; cdr ; cdr = cdr->next) {
01027 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01028 ast_copy_string(cdr->peeraccount, chan->peeraccount, sizeof(cdr->peeraccount));
01029 }
01030 }
01031
01032 ast_manager_event(chan, EVENT_FLAG_CALL, "NewPeerAccount",
01033 "Channel: %s\r\n"
01034 "Uniqueid: %s\r\n"
01035 "PeerAccount: %s\r\n"
01036 "OldPeerAccount: %s\r\n",
01037 chan->name, chan->uniqueid, chan->peeraccount, old_acct);
01038
01039 return 0;
01040 }
01041
01042 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
01043 {
01044 struct ast_cdr *cdr;
01045 int newflag = ast_cdr_amaflags2int(flag);
01046 if (newflag) {
01047 for (cdr = chan->cdr; cdr; cdr = cdr->next) {
01048 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01049 cdr->amaflags = newflag;
01050 }
01051 }
01052 }
01053
01054 return 0;
01055 }
01056
01057 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
01058 {
01059 struct ast_cdr *cdr = chan->cdr;
01060
01061 for ( ; cdr ; cdr = cdr->next) {
01062 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
01063 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
01064 }
01065
01066 return 0;
01067 }
01068
01069 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
01070 {
01071 struct ast_cdr *cdr = chan->cdr;
01072
01073 for ( ; cdr ; cdr = cdr->next) {
01074 int len = strlen(cdr->userfield);
01075
01076 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
01077 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
01078 }
01079
01080 return 0;
01081 }
01082
01083 int ast_cdr_update(struct ast_channel *c)
01084 {
01085 struct ast_cdr *cdr = c->cdr;
01086
01087 for ( ; cdr ; cdr = cdr->next) {
01088 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01089 set_one_cid(cdr, c);
01090
01091
01092 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
01093 ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
01094 ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
01095
01096
01097 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
01098 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
01099 }
01100 }
01101
01102 return 0;
01103 }
01104
01105 int ast_cdr_amaflags2int(const char *flag)
01106 {
01107 if (!strcasecmp(flag, "default"))
01108 return 0;
01109 if (!strcasecmp(flag, "omit"))
01110 return AST_CDR_OMIT;
01111 if (!strcasecmp(flag, "billing"))
01112 return AST_CDR_BILLING;
01113 if (!strcasecmp(flag, "documentation"))
01114 return AST_CDR_DOCUMENTATION;
01115 return -1;
01116 }
01117
01118 static void post_cdr(struct ast_cdr *cdr)
01119 {
01120 struct ast_cdr_beitem *i;
01121
01122 for ( ; cdr ; cdr = cdr->next) {
01123 if (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
01124
01125 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01126 continue;
01127 }
01128
01129
01130
01131
01132 if (ast_test_flag(cdr, AST_CDR_FLAG_DIALED) && !ast_test_flag(cdr, AST_CDR_FLAG_ORIGINATED)) {
01133 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01134 continue;
01135 }
01136
01137 check_post(cdr);
01138 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01139 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01140 continue;
01141 AST_RWLIST_RDLOCK(&be_list);
01142 AST_RWLIST_TRAVERSE(&be_list, i, list) {
01143 i->be(cdr);
01144 }
01145 AST_RWLIST_UNLOCK(&be_list);
01146 }
01147 }
01148
01149 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01150 {
01151 struct ast_cdr *duplicate;
01152 struct ast_flags flags = { 0 };
01153
01154 if (_flags)
01155 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01156
01157 for ( ; cdr ; cdr = cdr->next) {
01158
01159 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01160 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01161 ast_cdr_end(cdr);
01162 if ((duplicate = ast_cdr_dup_unique_swap(cdr))) {
01163 ast_cdr_detach(duplicate);
01164 }
01165 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01166 }
01167
01168
01169 if (ast_test_flag(&flags, AST_CDR_FLAG_POST_ENABLE)) {
01170 ast_clear_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01171 continue;
01172 }
01173
01174
01175 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01176 ast_cdr_free_vars(cdr, 0);
01177 }
01178
01179
01180 ast_clear_flag(cdr, AST_FLAGS_ALL);
01181 memset(&cdr->start, 0, sizeof(cdr->start));
01182 memset(&cdr->end, 0, sizeof(cdr->end));
01183 memset(&cdr->answer, 0, sizeof(cdr->answer));
01184 cdr->billsec = 0;
01185 cdr->duration = 0;
01186 ast_cdr_start(cdr);
01187 cdr->disposition = AST_CDR_NOANSWER;
01188 }
01189 }
01190 }
01191
01192 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01193 {
01194 struct ast_flags flags = { 0 };
01195
01196 if (_flags)
01197 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01198
01199
01200 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) {
01201 ast_clear_flag(cdr, AST_FLAGS_ALL);
01202 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01203 } else {
01204 ast_clear_flag(cdr, AST_FLAGS_ALL);
01205 }
01206
01207 memset(&cdr->start, 0, sizeof(cdr->start));
01208 memset(&cdr->end, 0, sizeof(cdr->end));
01209 memset(&cdr->answer, 0, sizeof(cdr->answer));
01210 cdr->billsec = 0;
01211 cdr->duration = 0;
01212 ast_cdr_start(cdr);
01213 cdr->disposition = AST_CDR_NULL;
01214 }
01215
01216 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01217 {
01218 struct ast_cdr *ret;
01219
01220 if (cdr) {
01221 ret = cdr;
01222
01223 while (cdr->next)
01224 cdr = cdr->next;
01225 cdr->next = newcdr;
01226 } else {
01227 ret = newcdr;
01228 }
01229
01230 return ret;
01231 }
01232
01233
01234 static void reset_batch(void)
01235 {
01236 batch->size = 0;
01237 batch->head = NULL;
01238 batch->tail = NULL;
01239 }
01240
01241
01242 static int init_batch(void)
01243 {
01244
01245 if (!(batch = ast_malloc(sizeof(*batch))))
01246 return -1;
01247
01248 reset_batch();
01249
01250 return 0;
01251 }
01252
01253 static void *do_batch_backend_process(void *data)
01254 {
01255 struct ast_cdr_batch_item *processeditem;
01256 struct ast_cdr_batch_item *batchitem = data;
01257
01258
01259 while (batchitem) {
01260 post_cdr(batchitem->cdr);
01261 ast_cdr_free(batchitem->cdr);
01262 processeditem = batchitem;
01263 batchitem = batchitem->next;
01264 ast_free(processeditem);
01265 }
01266
01267 return NULL;
01268 }
01269
01270 void ast_cdr_submit_batch(int do_shutdown)
01271 {
01272 struct ast_cdr_batch_item *oldbatchitems = NULL;
01273 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01274
01275
01276 if (!batch || !batch->head)
01277 return;
01278
01279
01280 ast_mutex_lock(&cdr_batch_lock);
01281 oldbatchitems = batch->head;
01282 reset_batch();
01283 ast_mutex_unlock(&cdr_batch_lock);
01284
01285
01286
01287 if (batchscheduleronly || do_shutdown) {
01288 ast_debug(1, "CDR single-threaded batch processing begins now\n");
01289 do_batch_backend_process(oldbatchitems);
01290 } else {
01291 if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
01292 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01293 do_batch_backend_process(oldbatchitems);
01294 } else {
01295 ast_debug(1, "CDR multi-threaded batch processing begins now\n");
01296 }
01297 }
01298 }
01299
01300 static int submit_scheduled_batch(const void *data)
01301 {
01302 ast_cdr_submit_batch(0);
01303
01304 ast_mutex_lock(&cdr_sched_lock);
01305 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01306 ast_mutex_unlock(&cdr_sched_lock);
01307
01308 return 0;
01309 }
01310
01311
01312 static void submit_unscheduled_batch(void)
01313 {
01314
01315 ast_mutex_lock(&cdr_sched_lock);
01316
01317 AST_SCHED_DEL(sched, cdr_sched);
01318
01319 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01320 ast_mutex_unlock(&cdr_sched_lock);
01321
01322
01323 ast_mutex_lock(&cdr_pending_lock);
01324 ast_cond_signal(&cdr_pending_cond);
01325 ast_mutex_unlock(&cdr_pending_lock);
01326 }
01327
01328 void ast_cdr_detach(struct ast_cdr *cdr)
01329 {
01330 struct ast_cdr_batch_item *newtail;
01331 int curr;
01332 int submit_batch = 0;
01333
01334 if (!cdr)
01335 return;
01336
01337
01338 if (!enabled) {
01339 ast_debug(1, "Dropping CDR !\n");
01340 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01341 ast_cdr_free(cdr);
01342 return;
01343 }
01344
01345
01346 if (!batchmode) {
01347 post_cdr(cdr);
01348 ast_cdr_free(cdr);
01349 return;
01350 }
01351
01352
01353 ast_debug(1, "CDR detaching from this thread\n");
01354
01355
01356 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01357 post_cdr(cdr);
01358 ast_cdr_free(cdr);
01359 return;
01360 }
01361
01362
01363 ast_mutex_lock(&cdr_batch_lock);
01364 if (!batch)
01365 init_batch();
01366 if (!batch->head) {
01367
01368 batch->head = newtail;
01369 } else {
01370
01371 batch->tail->next = newtail;
01372 }
01373 newtail->cdr = cdr;
01374 batch->tail = newtail;
01375 curr = batch->size++;
01376
01377
01378 if (curr >= (batchsize - 1)) {
01379 submit_batch = 1;
01380 }
01381 ast_mutex_unlock(&cdr_batch_lock);
01382
01383
01384 if (submit_batch) {
01385 submit_unscheduled_batch();
01386 }
01387 }
01388
01389 static void *do_cdr(void *data)
01390 {
01391 struct timespec timeout;
01392 int schedms;
01393 int numevents = 0;
01394
01395 for (;;) {
01396 struct timeval now;
01397 schedms = ast_sched_wait(sched);
01398
01399 if (schedms <= 0)
01400 schedms = 1000;
01401 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01402 timeout.tv_sec = now.tv_sec;
01403 timeout.tv_nsec = now.tv_usec * 1000;
01404
01405 ast_mutex_lock(&cdr_pending_lock);
01406 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01407 numevents = ast_sched_runq(sched);
01408 ast_mutex_unlock(&cdr_pending_lock);
01409 ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01410 }
01411
01412 return NULL;
01413 }
01414
01415 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01416 {
01417 struct ast_cdr_beitem *beitem=NULL;
01418 int cnt=0;
01419 long nextbatchtime=0;
01420
01421 switch (cmd) {
01422 case CLI_INIT:
01423 e->command = "cdr show status";
01424 e->usage =
01425 "Usage: cdr show status\n"
01426 " Displays the Call Detail Record engine system status.\n";
01427 return NULL;
01428 case CLI_GENERATE:
01429 return NULL;
01430 }
01431
01432 if (a->argc > 3)
01433 return CLI_SHOWUSAGE;
01434
01435 ast_cli(a->fd, "\n");
01436 ast_cli(a->fd, "Call Detail Record (CDR) settings\n");
01437 ast_cli(a->fd, "----------------------------------\n");
01438 ast_cli(a->fd, " Logging: %s\n", enabled ? "Enabled" : "Disabled");
01439 ast_cli(a->fd, " Mode: %s\n", batchmode ? "Batch" : "Simple");
01440 if (enabled) {
01441 ast_cli(a->fd, " Log unanswered calls: %s\n\n", unanswered ? "Yes" : "No");
01442 if (batchmode) {
01443 ast_cli(a->fd, "* Batch Mode Settings\n");
01444 ast_cli(a->fd, " -------------------\n");
01445 if (batch)
01446 cnt = batch->size;
01447 if (cdr_sched > -1)
01448 nextbatchtime = ast_sched_when(sched, cdr_sched);
01449 ast_cli(a->fd, " Safe shutdown: %s\n", batchsafeshutdown ? "Enabled" : "Disabled");
01450 ast_cli(a->fd, " Threading model: %s\n", batchscheduleronly ? "Scheduler only" : "Scheduler plus separate threads");
01451 ast_cli(a->fd, " Current batch size: %d record%s\n", cnt, ESS(cnt));
01452 ast_cli(a->fd, " Maximum batch size: %d record%s\n", batchsize, ESS(batchsize));
01453 ast_cli(a->fd, " Maximum batch time: %d second%s\n", batchtime, ESS(batchtime));
01454 ast_cli(a->fd, " Next batch processing time: %ld second%s\n\n", nextbatchtime, ESS(nextbatchtime));
01455 }
01456 ast_cli(a->fd, "* Registered Backends\n");
01457 ast_cli(a->fd, " -------------------\n");
01458 AST_RWLIST_RDLOCK(&be_list);
01459 if (AST_RWLIST_EMPTY(&be_list)) {
01460 ast_cli(a->fd, " (none)\n");
01461 } else {
01462 AST_RWLIST_TRAVERSE(&be_list, beitem, list) {
01463 ast_cli(a->fd, " %s\n", beitem->name);
01464 }
01465 }
01466 AST_RWLIST_UNLOCK(&be_list);
01467 ast_cli(a->fd, "\n");
01468 }
01469
01470 return CLI_SUCCESS;
01471 }
01472
01473 static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01474 {
01475 switch (cmd) {
01476 case CLI_INIT:
01477 e->command = "cdr submit";
01478 e->usage =
01479 "Usage: cdr submit\n"
01480 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
01481 return NULL;
01482 case CLI_GENERATE:
01483 return NULL;
01484 }
01485 if (a->argc > 2)
01486 return CLI_SHOWUSAGE;
01487
01488 submit_unscheduled_batch();
01489 ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01490
01491 return CLI_SUCCESS;
01492 }
01493
01494 static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
01495 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
01496
01497 static void do_reload(int reload)
01498 {
01499 struct ast_config *config;
01500 const char *enabled_value;
01501 const char *unanswered_value;
01502 const char *batched_value;
01503 const char *scheduleronly_value;
01504 const char *batchsafeshutdown_value;
01505 const char *size_value;
01506 const char *time_value;
01507 const char *end_before_h_value;
01508 const char *initiatedseconds_value;
01509 int cfg_size;
01510 int cfg_time;
01511 int was_enabled;
01512 int was_batchmode;
01513 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01514
01515 if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
01516 return;
01517 }
01518
01519 ast_mutex_lock(&cdr_batch_lock);
01520
01521 was_enabled = enabled;
01522 was_batchmode = batchmode;
01523
01524 batchsize = BATCH_SIZE_DEFAULT;
01525 batchtime = BATCH_TIME_DEFAULT;
01526 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01527 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01528 enabled = ENABLED_DEFAULT;
01529 batchmode = BATCHMODE_DEFAULT;
01530 unanswered = UNANSWERED_DEFAULT;
01531
01532 if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01533 ast_mutex_unlock(&cdr_batch_lock);
01534 return;
01535 }
01536
01537
01538 ast_mutex_lock(&cdr_sched_lock);
01539 AST_SCHED_DEL(sched, cdr_sched);
01540 ast_mutex_unlock(&cdr_sched_lock);
01541
01542 if (config) {
01543 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01544 enabled = ast_true(enabled_value);
01545 }
01546 if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
01547 unanswered = ast_true(unanswered_value);
01548 }
01549 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01550 batchmode = ast_true(batched_value);
01551 }
01552 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01553 batchscheduleronly = ast_true(scheduleronly_value);
01554 }
01555 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01556 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01557 }
01558 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01559 if (sscanf(size_value, "%30d", &cfg_size) < 1)
01560 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01561 else if (cfg_size < 0)
01562 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01563 else
01564 batchsize = cfg_size;
01565 }
01566 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01567 if (sscanf(time_value, "%30d", &cfg_time) < 1)
01568 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01569 else if (cfg_time < 0)
01570 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01571 else
01572 batchtime = cfg_time;
01573 }
01574 if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01575 ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01576 if ((initiatedseconds_value = ast_variable_retrieve(config, "general", "initiatedseconds")))
01577 ast_set2_flag(&ast_options, ast_true(initiatedseconds_value), AST_OPT_FLAG_INITIATED_SECONDS);
01578 }
01579
01580 if (enabled && !batchmode) {
01581 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01582 } else if (enabled && batchmode) {
01583 ast_mutex_lock(&cdr_sched_lock);
01584 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01585 ast_mutex_unlock(&cdr_sched_lock);
01586 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01587 } else {
01588 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01589 }
01590
01591
01592
01593 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01594 ast_cond_init(&cdr_pending_cond, NULL);
01595 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01596 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01597 ast_mutex_lock(&cdr_sched_lock);
01598 AST_SCHED_DEL(sched, cdr_sched);
01599 ast_mutex_unlock(&cdr_sched_lock);
01600 } else {
01601 ast_cli_register(&cli_submit);
01602 ast_register_atexit(ast_cdr_engine_term);
01603 }
01604
01605
01606 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01607
01608 pthread_cancel(cdr_thread);
01609 pthread_kill(cdr_thread, SIGURG);
01610 pthread_join(cdr_thread, NULL);
01611 cdr_thread = AST_PTHREADT_NULL;
01612 ast_cond_destroy(&cdr_pending_cond);
01613 ast_cli_unregister(&cli_submit);
01614 ast_unregister_atexit(ast_cdr_engine_term);
01615
01616
01617 if (!batchmode && was_batchmode) {
01618 ast_cdr_engine_term();
01619 }
01620 }
01621
01622 ast_mutex_unlock(&cdr_batch_lock);
01623 ast_config_destroy(config);
01624 manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
01625 }
01626
01627 static void cdr_engine_shutdown(void)
01628 {
01629 if (cdr_thread != AST_PTHREADT_NULL) {
01630
01631 pthread_cancel(cdr_thread);
01632 pthread_kill(cdr_thread, SIGURG);
01633 pthread_join(cdr_thread, NULL);
01634 cdr_thread = AST_PTHREADT_NULL;
01635 ast_cond_destroy(&cdr_pending_cond);
01636 }
01637 ast_cli_unregister(&cli_submit);
01638
01639 ast_cli_unregister(&cli_status);
01640 sched_context_destroy(sched);
01641 sched = NULL;
01642 ast_free(batch);
01643 batch = NULL;
01644 }
01645
01646 int ast_cdr_engine_init(void)
01647 {
01648 sched = sched_context_create();
01649 if (!sched) {
01650 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01651 return -1;
01652 }
01653
01654 ast_cli_register(&cli_status);
01655 do_reload(0);
01656 ast_register_atexit(cdr_engine_shutdown);
01657
01658 return 0;
01659 }
01660
01661
01662
01663 void ast_cdr_engine_term(void)
01664 {
01665 ast_cdr_submit_batch(batchsafeshutdown);
01666 }
01667
01668 int ast_cdr_engine_reload(void)
01669 {
01670 do_reload(1);
01671 return 0;
01672 }
01673
01674 int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)
01675 {
01676 struct ast_cdr *tmpcdr;
01677 struct ast_data *level;
01678 struct ast_var_t *variables;
01679 const char *var, *val;
01680 int x = 1, i;
01681 char workspace[256];
01682 char *tmp;
01683
01684 if (!cdr) {
01685 return -1;
01686 }
01687
01688 for (tmpcdr = cdr; tmpcdr; tmpcdr = (recur ? tmpcdr->next : NULL)) {
01689 level = ast_data_add_node(tree, "level");
01690 if (!level) {
01691 continue;
01692 }
01693
01694 ast_data_add_int(level, "level_number", x);
01695
01696 AST_LIST_TRAVERSE(&tmpcdr->varshead, variables, entries) {
01697 if (variables && (var = ast_var_name(variables)) &&
01698 (val = ast_var_value(variables)) && !ast_strlen_zero(var)
01699 && !ast_strlen_zero(val)) {
01700 ast_data_add_str(level, var, val);
01701 } else {
01702 break;
01703 }
01704 }
01705
01706 for (i = 0; cdr_readonly_vars[i]; i++) {
01707 workspace[0] = 0;
01708 ast_cdr_getvar(tmpcdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
01709 if (!tmp) {
01710 continue;
01711 }
01712 ast_data_add_str(level, cdr_readonly_vars[i], tmp);
01713 }
01714
01715 x++;
01716 }
01717
01718 return 0;
01719 }
01720