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