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