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