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