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