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: 140747 $")
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, *val;
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 (variables &&
00388 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00389 !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00390 if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, var, delim, val, sep)) {
00391 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00392 break;
00393 } else
00394 total++;
00395 } else
00396 break;
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 if (cdr->disposition < AST_CDR_BUSY)
00728 cdr->disposition = AST_CDR_BUSY;
00729 }
00730 }
00731 }
00732
00733 void ast_cdr_failed(struct ast_cdr *cdr)
00734 {
00735 for (; cdr; cdr = cdr->next) {
00736 check_post(cdr);
00737 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00738 if (cdr->disposition < AST_CDR_FAILED)
00739 cdr->disposition = AST_CDR_FAILED;
00740 }
00741 }
00742 }
00743
00744 void ast_cdr_noanswer(struct ast_cdr *cdr)
00745 {
00746 char *chan;
00747
00748 while (cdr) {
00749 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00750 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00751 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00752 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00753 if (cdr->disposition < AST_CDR_NOANSWER)
00754 cdr->disposition = AST_CDR_NOANSWER;
00755 }
00756 cdr = cdr->next;
00757 }
00758 }
00759
00760
00761
00762
00763 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00764 {
00765 int res = 0;
00766
00767 for (; cdr; cdr = cdr->next) {
00768 switch(cause) {
00769
00770 case AST_CAUSE_BUSY:
00771 ast_cdr_busy(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
00804 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
00805 {
00806
00807 const char *num = S_OR(c->cid.cid_ani, c->cid.cid_num);
00808 if (!cdr)
00809 return;
00810 if (!ast_strlen_zero(c->cid.cid_name)) {
00811 if (!ast_strlen_zero(num))
00812 snprintf(cdr->clid, sizeof(cdr->clid), "\"%s\" <%s>", c->cid.cid_name, num);
00813 else
00814 ast_copy_string(cdr->clid, c->cid.cid_name, sizeof(cdr->clid));
00815 } else if (!ast_strlen_zero(num)) {
00816 ast_copy_string(cdr->clid, num, sizeof(cdr->clid));
00817 } else {
00818 cdr->clid[0] = '\0';
00819 }
00820 ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
00821
00822 }
00823 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00824 {
00825 for (; cdr; cdr = cdr->next) {
00826 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00827 set_one_cid(cdr, c);
00828 }
00829 return 0;
00830 }
00831
00832 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00833 {
00834 char *chan;
00835
00836 for ( ; cdr ; cdr = cdr->next) {
00837 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00838 chan = S_OR(cdr->channel, "<unknown>");
00839 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00840 set_one_cid(cdr, c);
00841
00842 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL;
00843 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
00844 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00845
00846 ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
00847 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
00848
00849 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00850 }
00851 }
00852 return 0;
00853 }
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867 void ast_cdr_end(struct ast_cdr *cdr)
00868 {
00869 for ( ; cdr ; cdr = cdr->next) {
00870 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00871 continue;
00872 check_post(cdr);
00873 if (ast_tvzero(cdr->end))
00874 cdr->end = ast_tvnow();
00875 if (ast_tvzero(cdr->start)) {
00876 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00877 cdr->disposition = AST_CDR_FAILED;
00878 } else
00879 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00880 if (ast_tvzero(cdr->answer)) {
00881 if (cdr->disposition == AST_CDR_ANSWERED) {
00882 ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
00883 cdr->disposition = AST_CDR_FAILED;
00884 }
00885 } else
00886 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
00887 }
00888 }
00889
00890 char *ast_cdr_disp2str(int disposition)
00891 {
00892 switch (disposition) {
00893 case AST_CDR_NULL:
00894 return "NO ANSWER";
00895 case AST_CDR_NOANSWER:
00896 return "NO ANSWER";
00897 case AST_CDR_FAILED:
00898 return "FAILED";
00899 case AST_CDR_BUSY:
00900 return "BUSY";
00901 case AST_CDR_ANSWERED:
00902 return "ANSWERED";
00903 }
00904 return "UNKNOWN";
00905 }
00906
00907
00908 char *ast_cdr_flags2str(int flag)
00909 {
00910 switch(flag) {
00911 case AST_CDR_OMIT:
00912 return "OMIT";
00913 case AST_CDR_BILLING:
00914 return "BILLING";
00915 case AST_CDR_DOCUMENTATION:
00916 return "DOCUMENTATION";
00917 }
00918 return "Unknown";
00919 }
00920
00921 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00922 {
00923 struct ast_cdr *cdr = chan->cdr;
00924
00925 ast_string_field_set(chan, accountcode, account);
00926 for ( ; cdr ; cdr = cdr->next) {
00927 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00928 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
00929 }
00930 }
00931 return 0;
00932 }
00933
00934 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
00935 {
00936 struct ast_cdr *cdr;
00937 int newflag = ast_cdr_amaflags2int(flag);
00938 if (newflag) {
00939 for (cdr = chan->cdr; cdr; cdr = cdr->next) {
00940 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00941 cdr->amaflags = newflag;
00942 }
00943 }
00944 }
00945
00946 return 0;
00947 }
00948
00949 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
00950 {
00951 struct ast_cdr *cdr = chan->cdr;
00952
00953 for ( ; cdr ; cdr = cdr->next) {
00954 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00955 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
00956 }
00957
00958 return 0;
00959 }
00960
00961 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
00962 {
00963 struct ast_cdr *cdr = chan->cdr;
00964
00965 for ( ; cdr ; cdr = cdr->next) {
00966 int len = strlen(cdr->userfield);
00967
00968 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00969 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
00970 }
00971
00972 return 0;
00973 }
00974
00975 int ast_cdr_update(struct ast_channel *c)
00976 {
00977 struct ast_cdr *cdr = c->cdr;
00978
00979 for ( ; cdr ; cdr = cdr->next) {
00980 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00981 set_one_cid(cdr, c);
00982
00983
00984 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00985
00986
00987 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
00988 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
00989 }
00990 }
00991
00992 return 0;
00993 }
00994
00995 int ast_cdr_amaflags2int(const char *flag)
00996 {
00997 if (!strcasecmp(flag, "default"))
00998 return 0;
00999 if (!strcasecmp(flag, "omit"))
01000 return AST_CDR_OMIT;
01001 if (!strcasecmp(flag, "billing"))
01002 return AST_CDR_BILLING;
01003 if (!strcasecmp(flag, "documentation"))
01004 return AST_CDR_DOCUMENTATION;
01005 return -1;
01006 }
01007
01008 static void post_cdr(struct ast_cdr *cdr)
01009 {
01010 char *chan;
01011 struct ast_cdr_beitem *i;
01012
01013 for ( ; cdr ; cdr = cdr->next) {
01014 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))) {
01015
01016 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01017 continue;
01018 }
01019
01020 chan = S_OR(cdr->channel, "<unknown>");
01021 check_post(cdr);
01022 if (option_verbose > 1 && ast_tvzero(cdr->end))
01023 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
01024 if (option_verbose > 1 && ast_tvzero(cdr->start))
01025 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks start\n", chan);
01026 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01027 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01028 continue;
01029 AST_LIST_LOCK(&be_list);
01030 AST_LIST_TRAVERSE(&be_list, i, list) {
01031 i->be(cdr);
01032 }
01033 AST_LIST_UNLOCK(&be_list);
01034 }
01035 }
01036
01037 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01038 {
01039 struct ast_cdr *dup;
01040 struct ast_flags flags = { 0 };
01041
01042 if (_flags)
01043 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01044
01045 for ( ; cdr ; cdr = cdr->next) {
01046
01047 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01048 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01049 ast_cdr_end(cdr);
01050 if ((dup = ast_cdr_dup(cdr))) {
01051 ast_cdr_detach(dup);
01052 }
01053 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01054 }
01055
01056
01057 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01058 ast_cdr_free_vars(cdr, 0);
01059 }
01060
01061
01062 ast_clear_flag(cdr, AST_FLAGS_ALL);
01063 memset(&cdr->start, 0, sizeof(cdr->start));
01064 memset(&cdr->end, 0, sizeof(cdr->end));
01065 memset(&cdr->answer, 0, sizeof(cdr->answer));
01066 cdr->billsec = 0;
01067 cdr->duration = 0;
01068 ast_cdr_start(cdr);
01069 cdr->disposition = AST_CDR_NULL;
01070 }
01071 }
01072 }
01073
01074 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01075 {
01076 struct ast_flags flags = { 0 };
01077
01078 if (_flags)
01079 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01080
01081
01082 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) {
01083 ast_clear_flag(cdr, AST_FLAGS_ALL);
01084 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01085 } else {
01086 ast_clear_flag(cdr, AST_FLAGS_ALL);
01087 }
01088
01089 memset(&cdr->start, 0, sizeof(cdr->start));
01090 memset(&cdr->end, 0, sizeof(cdr->end));
01091 memset(&cdr->answer, 0, sizeof(cdr->answer));
01092 cdr->billsec = 0;
01093 cdr->duration = 0;
01094 ast_cdr_start(cdr);
01095 cdr->disposition = AST_CDR_NULL;
01096 }
01097
01098 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01099 {
01100 struct ast_cdr *ret;
01101
01102 if (cdr) {
01103 ret = cdr;
01104
01105 while (cdr->next)
01106 cdr = cdr->next;
01107 cdr->next = newcdr;
01108 } else {
01109 ret = newcdr;
01110 }
01111
01112 return ret;
01113 }
01114
01115
01116 static void reset_batch(void)
01117 {
01118 batch->size = 0;
01119 batch->head = NULL;
01120 batch->tail = NULL;
01121 }
01122
01123
01124 static int init_batch(void)
01125 {
01126
01127 if (!(batch = ast_malloc(sizeof(*batch))))
01128 return -1;
01129
01130 reset_batch();
01131
01132 return 0;
01133 }
01134
01135 static void *do_batch_backend_process(void *data)
01136 {
01137 struct ast_cdr_batch_item *processeditem;
01138 struct ast_cdr_batch_item *batchitem = data;
01139
01140
01141 while (batchitem) {
01142 post_cdr(batchitem->cdr);
01143 ast_cdr_free(batchitem->cdr);
01144 processeditem = batchitem;
01145 batchitem = batchitem->next;
01146 free(processeditem);
01147 }
01148
01149 return NULL;
01150 }
01151
01152 void ast_cdr_submit_batch(int shutdown)
01153 {
01154 struct ast_cdr_batch_item *oldbatchitems = NULL;
01155 pthread_attr_t attr;
01156 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01157
01158
01159 if (!batch || !batch->head)
01160 return;
01161
01162
01163 ast_mutex_lock(&cdr_batch_lock);
01164 oldbatchitems = batch->head;
01165 reset_batch();
01166 ast_mutex_unlock(&cdr_batch_lock);
01167
01168
01169
01170 if (batchscheduleronly || shutdown) {
01171 if (option_debug)
01172 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
01173 do_batch_backend_process(oldbatchitems);
01174 } else {
01175 pthread_attr_init(&attr);
01176 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01177 if (ast_pthread_create_background(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
01178 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01179 do_batch_backend_process(oldbatchitems);
01180 } else {
01181 if (option_debug)
01182 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
01183 }
01184 pthread_attr_destroy(&attr);
01185 }
01186 }
01187
01188 static int submit_scheduled_batch(const void *data)
01189 {
01190 ast_cdr_submit_batch(0);
01191
01192 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01193
01194 return 0;
01195 }
01196
01197 static void submit_unscheduled_batch(void)
01198 {
01199
01200 AST_SCHED_DEL(sched, cdr_sched);
01201
01202 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01203
01204 ast_mutex_lock(&cdr_pending_lock);
01205 ast_cond_signal(&cdr_pending_cond);
01206 ast_mutex_unlock(&cdr_pending_lock);
01207 }
01208
01209 void ast_cdr_detach(struct ast_cdr *cdr)
01210 {
01211 struct ast_cdr_batch_item *newtail;
01212 int curr;
01213
01214 if (!cdr)
01215 return;
01216
01217
01218 if (!enabled) {
01219 if (option_debug)
01220 ast_log(LOG_DEBUG, "Dropping CDR !\n");
01221 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01222 ast_cdr_free(cdr);
01223 return;
01224 }
01225
01226
01227 if (!batchmode) {
01228 post_cdr(cdr);
01229 ast_cdr_free(cdr);
01230 return;
01231 }
01232
01233
01234 if (option_debug)
01235 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
01236
01237
01238 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01239 post_cdr(cdr);
01240 ast_cdr_free(cdr);
01241 return;
01242 }
01243
01244
01245 ast_mutex_lock(&cdr_batch_lock);
01246 if (!batch)
01247 init_batch();
01248 if (!batch->head) {
01249
01250 batch->head = newtail;
01251 } else {
01252
01253 batch->tail->next = newtail;
01254 }
01255 newtail->cdr = cdr;
01256 batch->tail = newtail;
01257 curr = batch->size++;
01258 ast_mutex_unlock(&cdr_batch_lock);
01259
01260
01261 if (curr >= (batchsize - 1))
01262 submit_unscheduled_batch();
01263 }
01264
01265 static void *do_cdr(void *data)
01266 {
01267 struct timespec timeout;
01268 int schedms;
01269 int numevents = 0;
01270
01271 for(;;) {
01272 struct timeval now;
01273 schedms = ast_sched_wait(sched);
01274
01275 if (schedms <= 0)
01276 schedms = 1000;
01277 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01278 timeout.tv_sec = now.tv_sec;
01279 timeout.tv_nsec = now.tv_usec * 1000;
01280
01281 ast_mutex_lock(&cdr_pending_lock);
01282 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01283 numevents = ast_sched_runq(sched);
01284 ast_mutex_unlock(&cdr_pending_lock);
01285 if (option_debug > 1)
01286 ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01287 }
01288
01289 return NULL;
01290 }
01291
01292 static int handle_cli_status(int fd, int argc, char *argv[])
01293 {
01294 struct ast_cdr_beitem *beitem=NULL;
01295 int cnt=0;
01296 long nextbatchtime=0;
01297
01298 if (argc > 2)
01299 return RESULT_SHOWUSAGE;
01300
01301 ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
01302 ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
01303 if (enabled) {
01304 ast_cli(fd, "CDR output unanswered calls: %s\n", unanswered ? "yes" : "no");
01305 if (batchmode) {
01306 if (batch)
01307 cnt = batch->size;
01308 if (cdr_sched > -1)
01309 nextbatchtime = ast_sched_when(sched, cdr_sched);
01310 ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
01311 ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
01312 ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
01313 ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
01314 ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
01315 ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
01316 }
01317 AST_LIST_LOCK(&be_list);
01318 AST_LIST_TRAVERSE(&be_list, beitem, list) {
01319 ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
01320 }
01321 AST_LIST_UNLOCK(&be_list);
01322 }
01323
01324 return 0;
01325 }
01326
01327 static int handle_cli_submit(int fd, int argc, char *argv[])
01328 {
01329 if (argc > 2)
01330 return RESULT_SHOWUSAGE;
01331
01332 submit_unscheduled_batch();
01333 ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01334
01335 return 0;
01336 }
01337
01338 static struct ast_cli_entry cli_submit = {
01339 { "cdr", "submit", NULL },
01340 handle_cli_submit, "Posts all pending batched CDR data",
01341 "Usage: cdr submit\n"
01342 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
01343 };
01344
01345 static struct ast_cli_entry cli_status = {
01346 { "cdr", "status", NULL },
01347 handle_cli_status, "Display the CDR status",
01348 "Usage: cdr status\n"
01349 " Displays the Call Detail Record engine system status.\n"
01350 };
01351
01352 static int do_reload(void)
01353 {
01354 struct ast_config *config;
01355 const char *enabled_value;
01356 const char *unanswered_value;
01357 const char *batched_value;
01358 const char *scheduleronly_value;
01359 const char *batchsafeshutdown_value;
01360 const char *size_value;
01361 const char *time_value;
01362 const char *end_before_h_value;
01363 int cfg_size;
01364 int cfg_time;
01365 int was_enabled;
01366 int was_batchmode;
01367 int res=0;
01368
01369 ast_mutex_lock(&cdr_batch_lock);
01370
01371 batchsize = BATCH_SIZE_DEFAULT;
01372 batchtime = BATCH_TIME_DEFAULT;
01373 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01374 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01375 was_enabled = enabled;
01376 was_batchmode = batchmode;
01377 enabled = 1;
01378 batchmode = 0;
01379
01380
01381 AST_SCHED_DEL(sched, cdr_sched);
01382
01383 if ((config = ast_config_load("cdr.conf"))) {
01384 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01385 enabled = ast_true(enabled_value);
01386 }
01387 if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
01388 unanswered = ast_true(unanswered_value);
01389 }
01390 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01391 batchmode = ast_true(batched_value);
01392 }
01393 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01394 batchscheduleronly = ast_true(scheduleronly_value);
01395 }
01396 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01397 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01398 }
01399 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01400 if (sscanf(size_value, "%d", &cfg_size) < 1)
01401 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01402 else if (size_value < 0)
01403 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01404 else
01405 batchsize = cfg_size;
01406 }
01407 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01408 if (sscanf(time_value, "%d", &cfg_time) < 1)
01409 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01410 else if (time_value < 0)
01411 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01412 else
01413 batchtime = cfg_time;
01414 }
01415 if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01416 ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01417 }
01418
01419 if (enabled && !batchmode) {
01420 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01421 } else if (enabled && batchmode) {
01422 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01423 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01424 } else {
01425 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01426 }
01427
01428
01429
01430 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01431 ast_cond_init(&cdr_pending_cond, NULL);
01432 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01433 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01434 AST_SCHED_DEL(sched, cdr_sched);
01435 } else {
01436 ast_cli_register(&cli_submit);
01437 ast_register_atexit(ast_cdr_engine_term);
01438 res = 0;
01439 }
01440
01441
01442 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01443
01444 pthread_cancel(cdr_thread);
01445 pthread_kill(cdr_thread, SIGURG);
01446 pthread_join(cdr_thread, NULL);
01447 cdr_thread = AST_PTHREADT_NULL;
01448 ast_cond_destroy(&cdr_pending_cond);
01449 ast_cli_unregister(&cli_submit);
01450 ast_unregister_atexit(ast_cdr_engine_term);
01451 res = 0;
01452
01453
01454 if (!batchmode && was_batchmode) {
01455 ast_cdr_engine_term();
01456 }
01457 } else {
01458 res = 0;
01459 }
01460
01461 ast_mutex_unlock(&cdr_batch_lock);
01462 ast_config_destroy(config);
01463
01464 return res;
01465 }
01466
01467 int ast_cdr_engine_init(void)
01468 {
01469 int res;
01470
01471 sched = sched_context_create();
01472 if (!sched) {
01473 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01474 return -1;
01475 }
01476
01477 ast_cli_register(&cli_status);
01478
01479 res = do_reload();
01480 if (res) {
01481 ast_mutex_lock(&cdr_batch_lock);
01482 res = init_batch();
01483 ast_mutex_unlock(&cdr_batch_lock);
01484 }
01485
01486 return res;
01487 }
01488
01489
01490
01491 void ast_cdr_engine_term(void)
01492 {
01493 ast_cdr_submit_batch(batchsafeshutdown);
01494 }
01495
01496 int ast_cdr_engine_reload(void)
01497 {
01498 return do_reload();
01499 }
01500