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