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 }
00913 }
00914
00915 char *ast_cdr_disp2str(int disposition)
00916 {
00917 switch (disposition) {
00918 case AST_CDR_NULL:
00919 return "NO ANSWER";
00920 case AST_CDR_NOANSWER:
00921 return "NO ANSWER";
00922 case AST_CDR_FAILED:
00923 return "FAILED";
00924 case AST_CDR_BUSY:
00925 return "BUSY";
00926 case AST_CDR_ANSWERED:
00927 return "ANSWERED";
00928 }
00929 return "UNKNOWN";
00930 }
00931
00932
00933 char *ast_cdr_flags2str(int flag)
00934 {
00935 switch(flag) {
00936 case AST_CDR_OMIT:
00937 return "OMIT";
00938 case AST_CDR_BILLING:
00939 return "BILLING";
00940 case AST_CDR_DOCUMENTATION:
00941 return "DOCUMENTATION";
00942 }
00943 return "Unknown";
00944 }
00945
00946 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00947 {
00948 struct ast_cdr *cdr = chan->cdr;
00949
00950 ast_string_field_set(chan, accountcode, account);
00951 for ( ; cdr ; cdr = cdr->next) {
00952 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00953 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
00954 }
00955 }
00956 return 0;
00957 }
00958
00959 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
00960 {
00961 struct ast_cdr *cdr;
00962 int newflag = ast_cdr_amaflags2int(flag);
00963 if (newflag) {
00964 for (cdr = chan->cdr; cdr; cdr = cdr->next) {
00965 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00966 cdr->amaflags = newflag;
00967 }
00968 }
00969 }
00970
00971 return 0;
00972 }
00973
00974 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
00975 {
00976 struct ast_cdr *cdr = chan->cdr;
00977
00978 for ( ; cdr ; cdr = cdr->next) {
00979 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00980 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
00981 }
00982
00983 return 0;
00984 }
00985
00986 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
00987 {
00988 struct ast_cdr *cdr = chan->cdr;
00989
00990 for ( ; cdr ; cdr = cdr->next) {
00991 int len = strlen(cdr->userfield);
00992
00993 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00994 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
00995 }
00996
00997 return 0;
00998 }
00999
01000 int ast_cdr_update(struct ast_channel *c)
01001 {
01002 struct ast_cdr *cdr = c->cdr;
01003
01004 for ( ; cdr ; cdr = cdr->next) {
01005 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01006 set_one_cid(cdr, c);
01007
01008
01009 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
01010
01011
01012 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
01013 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
01014 }
01015 }
01016
01017 return 0;
01018 }
01019
01020 int ast_cdr_amaflags2int(const char *flag)
01021 {
01022 if (!strcasecmp(flag, "default"))
01023 return 0;
01024 if (!strcasecmp(flag, "omit"))
01025 return AST_CDR_OMIT;
01026 if (!strcasecmp(flag, "billing"))
01027 return AST_CDR_BILLING;
01028 if (!strcasecmp(flag, "documentation"))
01029 return AST_CDR_DOCUMENTATION;
01030 return -1;
01031 }
01032
01033 static void post_cdr(struct ast_cdr *cdr)
01034 {
01035 char *chan;
01036 struct ast_cdr_beitem *i;
01037
01038 for ( ; cdr ; cdr = cdr->next) {
01039 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))) {
01040
01041 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01042 continue;
01043 }
01044
01045
01046
01047
01048 if (ast_test_flag(cdr, AST_CDR_FLAG_DIALED) && !ast_test_flag(cdr, AST_CDR_FLAG_ORIGINATED)) {
01049 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01050 continue;
01051 }
01052
01053 chan = S_OR(cdr->channel, "<unknown>");
01054 check_post(cdr);
01055 if (option_verbose > 1 && ast_tvzero(cdr->end))
01056 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
01057 if (option_verbose > 1 && ast_tvzero(cdr->start))
01058 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks start\n", chan);
01059 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01060 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01061 continue;
01062 AST_LIST_LOCK(&be_list);
01063 AST_LIST_TRAVERSE(&be_list, i, list) {
01064 i->be(cdr);
01065 }
01066 AST_LIST_UNLOCK(&be_list);
01067 }
01068 }
01069
01070 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01071 {
01072 struct ast_cdr *dup;
01073 struct ast_flags flags = { 0 };
01074
01075 if (_flags)
01076 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01077
01078 for ( ; cdr ; cdr = cdr->next) {
01079
01080 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01081 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01082 ast_cdr_end(cdr);
01083 if ((dup = ast_cdr_dup(cdr))) {
01084 ast_cdr_detach(dup);
01085 }
01086 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01087 }
01088
01089
01090 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01091 ast_cdr_free_vars(cdr, 0);
01092 }
01093
01094
01095 ast_clear_flag(cdr, AST_FLAGS_ALL);
01096 memset(&cdr->start, 0, sizeof(cdr->start));
01097 memset(&cdr->end, 0, sizeof(cdr->end));
01098 memset(&cdr->answer, 0, sizeof(cdr->answer));
01099 cdr->billsec = 0;
01100 cdr->duration = 0;
01101 ast_cdr_start(cdr);
01102 cdr->disposition = AST_CDR_NOANSWER;
01103 }
01104 }
01105 }
01106
01107 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01108 {
01109 struct ast_flags flags = { 0 };
01110
01111 if (_flags)
01112 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01113
01114
01115 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) {
01116 ast_clear_flag(cdr, AST_FLAGS_ALL);
01117 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01118 } else {
01119 ast_clear_flag(cdr, AST_FLAGS_ALL);
01120 }
01121
01122 memset(&cdr->start, 0, sizeof(cdr->start));
01123 memset(&cdr->end, 0, sizeof(cdr->end));
01124 memset(&cdr->answer, 0, sizeof(cdr->answer));
01125 cdr->billsec = 0;
01126 cdr->duration = 0;
01127 ast_cdr_start(cdr);
01128 cdr->disposition = AST_CDR_NULL;
01129 }
01130
01131 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01132 {
01133 struct ast_cdr *ret;
01134
01135 if (cdr) {
01136 ret = cdr;
01137
01138 while (cdr->next)
01139 cdr = cdr->next;
01140 cdr->next = newcdr;
01141 } else {
01142 ret = newcdr;
01143 }
01144
01145 return ret;
01146 }
01147
01148
01149 static void reset_batch(void)
01150 {
01151 batch->size = 0;
01152 batch->head = NULL;
01153 batch->tail = NULL;
01154 }
01155
01156
01157 static int init_batch(void)
01158 {
01159
01160 if (!(batch = ast_malloc(sizeof(*batch))))
01161 return -1;
01162
01163 reset_batch();
01164
01165 return 0;
01166 }
01167
01168 static void *do_batch_backend_process(void *data)
01169 {
01170 struct ast_cdr_batch_item *processeditem;
01171 struct ast_cdr_batch_item *batchitem = data;
01172
01173
01174 while (batchitem) {
01175 post_cdr(batchitem->cdr);
01176 ast_cdr_free(batchitem->cdr);
01177 processeditem = batchitem;
01178 batchitem = batchitem->next;
01179 free(processeditem);
01180 }
01181
01182 return NULL;
01183 }
01184
01185 void ast_cdr_submit_batch(int shutdown)
01186 {
01187 struct ast_cdr_batch_item *oldbatchitems = NULL;
01188 pthread_attr_t attr;
01189 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01190
01191
01192 if (!batch || !batch->head)
01193 return;
01194
01195
01196 ast_mutex_lock(&cdr_batch_lock);
01197 oldbatchitems = batch->head;
01198 reset_batch();
01199 ast_mutex_unlock(&cdr_batch_lock);
01200
01201
01202
01203 if (batchscheduleronly || shutdown) {
01204 if (option_debug)
01205 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
01206 do_batch_backend_process(oldbatchitems);
01207 } else {
01208 pthread_attr_init(&attr);
01209 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01210 if (ast_pthread_create_background(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
01211 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01212 do_batch_backend_process(oldbatchitems);
01213 } else {
01214 if (option_debug)
01215 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
01216 }
01217 pthread_attr_destroy(&attr);
01218 }
01219 }
01220
01221 static int submit_scheduled_batch(const void *data)
01222 {
01223 ast_cdr_submit_batch(0);
01224
01225 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01226
01227 return 0;
01228 }
01229
01230 static void submit_unscheduled_batch(void)
01231 {
01232
01233 AST_SCHED_DEL(sched, cdr_sched);
01234
01235 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01236
01237 ast_mutex_lock(&cdr_pending_lock);
01238 ast_cond_signal(&cdr_pending_cond);
01239 ast_mutex_unlock(&cdr_pending_lock);
01240 }
01241
01242 void ast_cdr_detach(struct ast_cdr *cdr)
01243 {
01244 struct ast_cdr_batch_item *newtail;
01245 int curr;
01246
01247 if (!cdr)
01248 return;
01249
01250
01251 if (!enabled) {
01252 if (option_debug)
01253 ast_log(LOG_DEBUG, "Dropping CDR !\n");
01254 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01255 ast_cdr_free(cdr);
01256 return;
01257 }
01258
01259
01260 if (!batchmode) {
01261 post_cdr(cdr);
01262 ast_cdr_free(cdr);
01263 return;
01264 }
01265
01266
01267 if (option_debug)
01268 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
01269
01270
01271 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01272 post_cdr(cdr);
01273 ast_cdr_free(cdr);
01274 return;
01275 }
01276
01277
01278 ast_mutex_lock(&cdr_batch_lock);
01279 if (!batch)
01280 init_batch();
01281 if (!batch->head) {
01282
01283 batch->head = newtail;
01284 } else {
01285
01286 batch->tail->next = newtail;
01287 }
01288 newtail->cdr = cdr;
01289 batch->tail = newtail;
01290 curr = batch->size++;
01291 ast_mutex_unlock(&cdr_batch_lock);
01292
01293
01294 if (curr >= (batchsize - 1))
01295 submit_unscheduled_batch();
01296 }
01297
01298 static void *do_cdr(void *data)
01299 {
01300 struct timespec timeout;
01301 int schedms;
01302 int numevents = 0;
01303
01304 for(;;) {
01305 struct timeval now;
01306 schedms = ast_sched_wait(sched);
01307
01308 if (schedms <= 0)
01309 schedms = 1000;
01310 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01311 timeout.tv_sec = now.tv_sec;
01312 timeout.tv_nsec = now.tv_usec * 1000;
01313
01314 ast_mutex_lock(&cdr_pending_lock);
01315 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01316 numevents = ast_sched_runq(sched);
01317 ast_mutex_unlock(&cdr_pending_lock);
01318 if (option_debug > 1)
01319 ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01320 }
01321
01322 return NULL;
01323 }
01324
01325 static int handle_cli_status(int fd, int argc, char *argv[])
01326 {
01327 struct ast_cdr_beitem *beitem=NULL;
01328 int cnt=0;
01329 long nextbatchtime=0;
01330
01331 if (argc > 2)
01332 return RESULT_SHOWUSAGE;
01333
01334 ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
01335 ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
01336 if (enabled) {
01337 ast_cli(fd, "CDR output unanswered calls: %s\n", unanswered ? "yes" : "no");
01338 if (batchmode) {
01339 if (batch)
01340 cnt = batch->size;
01341 if (cdr_sched > -1)
01342 nextbatchtime = ast_sched_when(sched, cdr_sched);
01343 ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
01344 ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
01345 ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
01346 ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
01347 ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
01348 ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
01349 }
01350 AST_LIST_LOCK(&be_list);
01351 AST_LIST_TRAVERSE(&be_list, beitem, list) {
01352 ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
01353 }
01354 AST_LIST_UNLOCK(&be_list);
01355 }
01356
01357 return 0;
01358 }
01359
01360 static int handle_cli_submit(int fd, int argc, char *argv[])
01361 {
01362 if (argc > 2)
01363 return RESULT_SHOWUSAGE;
01364
01365 submit_unscheduled_batch();
01366 ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01367
01368 return 0;
01369 }
01370
01371 static struct ast_cli_entry cli_submit = {
01372 { "cdr", "submit", NULL },
01373 handle_cli_submit, "Posts all pending batched CDR data",
01374 "Usage: cdr submit\n"
01375 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
01376 };
01377
01378 static struct ast_cli_entry cli_status = {
01379 { "cdr", "status", NULL },
01380 handle_cli_status, "Display the CDR status",
01381 "Usage: cdr status\n"
01382 " Displays the Call Detail Record engine system status.\n"
01383 };
01384
01385 static int do_reload(void)
01386 {
01387 struct ast_config *config;
01388 const char *enabled_value;
01389 const char *unanswered_value;
01390 const char *batched_value;
01391 const char *scheduleronly_value;
01392 const char *batchsafeshutdown_value;
01393 const char *size_value;
01394 const char *time_value;
01395 const char *end_before_h_value;
01396 int cfg_size;
01397 int cfg_time;
01398 int was_enabled;
01399 int was_batchmode;
01400 int res=0;
01401
01402 ast_mutex_lock(&cdr_batch_lock);
01403
01404 batchsize = BATCH_SIZE_DEFAULT;
01405 batchtime = BATCH_TIME_DEFAULT;
01406 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01407 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01408 was_enabled = enabled;
01409 was_batchmode = batchmode;
01410 enabled = 1;
01411 batchmode = 0;
01412
01413
01414 AST_SCHED_DEL(sched, cdr_sched);
01415
01416 if ((config = ast_config_load("cdr.conf"))) {
01417 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01418 enabled = ast_true(enabled_value);
01419 }
01420 if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
01421 unanswered = ast_true(unanswered_value);
01422 }
01423 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01424 batchmode = ast_true(batched_value);
01425 }
01426 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01427 batchscheduleronly = ast_true(scheduleronly_value);
01428 }
01429 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01430 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01431 }
01432 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01433 if (sscanf(size_value, "%30d", &cfg_size) < 1)
01434 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01435 else if (cfg_size < 0)
01436 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01437 else
01438 batchsize = cfg_size;
01439 }
01440 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01441 if (sscanf(time_value, "%30d", &cfg_time) < 1)
01442 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01443 else if (cfg_time < 0)
01444 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01445 else
01446 batchtime = cfg_time;
01447 }
01448 if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01449 ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01450 }
01451
01452 if (enabled && !batchmode) {
01453 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01454 } else if (enabled && batchmode) {
01455 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01456 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01457 } else {
01458 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01459 }
01460
01461
01462
01463 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01464 ast_cond_init(&cdr_pending_cond, NULL);
01465 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01466 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01467 AST_SCHED_DEL(sched, cdr_sched);
01468 } else {
01469 ast_cli_register(&cli_submit);
01470 ast_register_atexit(ast_cdr_engine_term);
01471 res = 0;
01472 }
01473
01474
01475 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01476
01477 pthread_cancel(cdr_thread);
01478 pthread_kill(cdr_thread, SIGURG);
01479 pthread_join(cdr_thread, NULL);
01480 cdr_thread = AST_PTHREADT_NULL;
01481 ast_cond_destroy(&cdr_pending_cond);
01482 ast_cli_unregister(&cli_submit);
01483 ast_unregister_atexit(ast_cdr_engine_term);
01484 res = 0;
01485
01486
01487 if (!batchmode && was_batchmode) {
01488 ast_cdr_engine_term();
01489 }
01490 } else {
01491 res = 0;
01492 }
01493
01494 ast_mutex_unlock(&cdr_batch_lock);
01495 ast_config_destroy(config);
01496
01497 return res;
01498 }
01499
01500 int ast_cdr_engine_init(void)
01501 {
01502 int res;
01503
01504 sched = sched_context_create();
01505 if (!sched) {
01506 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01507 return -1;
01508 }
01509
01510 ast_cli_register(&cli_status);
01511
01512 res = do_reload();
01513 if (res) {
01514 ast_mutex_lock(&cdr_batch_lock);
01515 res = init_batch();
01516 ast_mutex_unlock(&cdr_batch_lock);
01517 }
01518
01519 return res;
01520 }
01521
01522
01523
01524 void ast_cdr_engine_term(void)
01525 {
01526 ast_cdr_submit_batch(batchsafeshutdown);
01527 }
01528
01529 int ast_cdr_engine_reload(void)
01530 {
01531 return do_reload();
01532 }
01533