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
00883 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
00884 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
00885 }
00886 }
00887
00888 return 0;
00889 }
00890
00891 int ast_cdr_amaflags2int(const char *flag)
00892 {
00893 if (!strcasecmp(flag, "default"))
00894 return 0;
00895 if (!strcasecmp(flag, "omit"))
00896 return AST_CDR_OMIT;
00897 if (!strcasecmp(flag, "billing"))
00898 return AST_CDR_BILLING;
00899 if (!strcasecmp(flag, "documentation"))
00900 return AST_CDR_DOCUMENTATION;
00901 return -1;
00902 }
00903
00904 static void post_cdr(struct ast_cdr *cdr)
00905 {
00906 char *chan;
00907 struct ast_cdr_beitem *i;
00908
00909 for ( ; cdr ; cdr = cdr->next) {
00910 chan = S_OR(cdr->channel, "<unknown>");
00911 check_post(cdr);
00912 if (ast_tvzero(cdr->end))
00913 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
00914 if (ast_tvzero(cdr->start))
00915 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
00916 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
00917 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
00918 continue;
00919 AST_LIST_LOCK(&be_list);
00920 AST_LIST_TRAVERSE(&be_list, i, list) {
00921 i->be(cdr);
00922 }
00923 AST_LIST_UNLOCK(&be_list);
00924 }
00925 }
00926
00927 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
00928 {
00929 struct ast_cdr *dup;
00930 struct ast_flags flags = { 0 };
00931
00932 if (_flags)
00933 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
00934
00935 for ( ; cdr ; cdr = cdr->next) {
00936
00937 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00938 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
00939 ast_cdr_end(cdr);
00940 if ((dup = ast_cdr_dup(cdr))) {
00941 ast_cdr_detach(dup);
00942 }
00943 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
00944 }
00945
00946
00947 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
00948 ast_cdr_free_vars(cdr, 0);
00949 }
00950
00951
00952 ast_clear_flag(cdr, AST_FLAGS_ALL);
00953 memset(&cdr->start, 0, sizeof(cdr->start));
00954 memset(&cdr->end, 0, sizeof(cdr->end));
00955 memset(&cdr->answer, 0, sizeof(cdr->answer));
00956 cdr->billsec = 0;
00957 cdr->duration = 0;
00958 ast_cdr_start(cdr);
00959 cdr->disposition = AST_CDR_NOANSWER;
00960 }
00961 }
00962 }
00963
00964 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
00965 {
00966 struct ast_cdr *ret;
00967
00968 if (cdr) {
00969 ret = cdr;
00970
00971 while (cdr->next)
00972 cdr = cdr->next;
00973 cdr->next = newcdr;
00974 } else {
00975 ret = newcdr;
00976 }
00977
00978 return ret;
00979 }
00980
00981
00982 static void reset_batch(void)
00983 {
00984 batch->size = 0;
00985 batch->head = NULL;
00986 batch->tail = NULL;
00987 }
00988
00989
00990 static int init_batch(void)
00991 {
00992
00993 if (!(batch = ast_malloc(sizeof(*batch))))
00994 return -1;
00995
00996 reset_batch();
00997
00998 return 0;
00999 }
01000
01001 static void *do_batch_backend_process(void *data)
01002 {
01003 struct ast_cdr_batch_item *processeditem;
01004 struct ast_cdr_batch_item *batchitem = data;
01005
01006
01007 while (batchitem) {
01008 post_cdr(batchitem->cdr);
01009 ast_cdr_free(batchitem->cdr);
01010 processeditem = batchitem;
01011 batchitem = batchitem->next;
01012 free(processeditem);
01013 }
01014
01015 return NULL;
01016 }
01017
01018 void ast_cdr_submit_batch(int shutdown)
01019 {
01020 struct ast_cdr_batch_item *oldbatchitems = NULL;
01021 pthread_attr_t attr;
01022 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01023
01024
01025 if (!batch || !batch->head)
01026 return;
01027
01028
01029 ast_mutex_lock(&cdr_batch_lock);
01030 oldbatchitems = batch->head;
01031 reset_batch();
01032 ast_mutex_unlock(&cdr_batch_lock);
01033
01034
01035
01036 if (batchscheduleronly || shutdown) {
01037 if (option_debug)
01038 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
01039 do_batch_backend_process(oldbatchitems);
01040 } else {
01041 pthread_attr_init(&attr);
01042 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01043 if (ast_pthread_create_background(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
01044 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01045 do_batch_backend_process(oldbatchitems);
01046 } else {
01047 if (option_debug)
01048 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
01049 }
01050 pthread_attr_destroy(&attr);
01051 }
01052 }
01053
01054 static int submit_scheduled_batch(void *data)
01055 {
01056 ast_cdr_submit_batch(0);
01057
01058 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01059
01060 return 0;
01061 }
01062
01063 static void submit_unscheduled_batch(void)
01064 {
01065
01066 if (cdr_sched > -1)
01067 ast_sched_del(sched, cdr_sched);
01068
01069 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01070
01071 ast_mutex_lock(&cdr_pending_lock);
01072 ast_cond_signal(&cdr_pending_cond);
01073 ast_mutex_unlock(&cdr_pending_lock);
01074 }
01075
01076 void ast_cdr_detach(struct ast_cdr *cdr)
01077 {
01078 struct ast_cdr_batch_item *newtail;
01079 int curr;
01080
01081 if (!cdr)
01082 return;
01083
01084
01085 if (!enabled) {
01086 if (option_debug)
01087 ast_log(LOG_DEBUG, "Dropping CDR !\n");
01088 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01089 ast_cdr_free(cdr);
01090 return;
01091 }
01092
01093
01094 if (!batchmode) {
01095 post_cdr(cdr);
01096 ast_cdr_free(cdr);
01097 return;
01098 }
01099
01100
01101 if (option_debug)
01102 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
01103
01104
01105 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01106 post_cdr(cdr);
01107 ast_cdr_free(cdr);
01108 return;
01109 }
01110
01111
01112 ast_mutex_lock(&cdr_batch_lock);
01113 if (!batch)
01114 init_batch();
01115 if (!batch->head) {
01116
01117 batch->head = newtail;
01118 } else {
01119
01120 batch->tail->next = newtail;
01121 }
01122 newtail->cdr = cdr;
01123 batch->tail = newtail;
01124 curr = batch->size++;
01125 ast_mutex_unlock(&cdr_batch_lock);
01126
01127
01128 if (curr >= (batchsize - 1))
01129 submit_unscheduled_batch();
01130 }
01131
01132 static void *do_cdr(void *data)
01133 {
01134 struct timespec timeout;
01135 int schedms;
01136 int numevents = 0;
01137
01138 for(;;) {
01139 struct timeval now;
01140 schedms = ast_sched_wait(sched);
01141
01142 if (schedms <= 0)
01143 schedms = 1000;
01144 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01145 timeout.tv_sec = now.tv_sec;
01146 timeout.tv_nsec = now.tv_usec * 1000;
01147
01148 ast_mutex_lock(&cdr_pending_lock);
01149 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01150 numevents = ast_sched_runq(sched);
01151 ast_mutex_unlock(&cdr_pending_lock);
01152 if (option_debug > 1)
01153 ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01154 }
01155
01156 return NULL;
01157 }
01158
01159 static int handle_cli_status(int fd, int argc, char *argv[])
01160 {
01161 struct ast_cdr_beitem *beitem=NULL;
01162 int cnt=0;
01163 long nextbatchtime=0;
01164
01165 if (argc > 2)
01166 return RESULT_SHOWUSAGE;
01167
01168 ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
01169 ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
01170 if (enabled) {
01171 if (batchmode) {
01172 if (batch)
01173 cnt = batch->size;
01174 if (cdr_sched > -1)
01175 nextbatchtime = ast_sched_when(sched, cdr_sched);
01176 ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
01177 ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
01178 ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
01179 ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
01180 ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
01181 ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
01182 }
01183 AST_LIST_LOCK(&be_list);
01184 AST_LIST_TRAVERSE(&be_list, beitem, list) {
01185 ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
01186 }
01187 AST_LIST_UNLOCK(&be_list);
01188 }
01189
01190 return 0;
01191 }
01192
01193 static int handle_cli_submit(int fd, int argc, char *argv[])
01194 {
01195 if (argc > 2)
01196 return RESULT_SHOWUSAGE;
01197
01198 submit_unscheduled_batch();
01199 ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01200
01201 return 0;
01202 }
01203
01204 static struct ast_cli_entry cli_submit = {
01205 { "cdr", "submit", NULL },
01206 handle_cli_submit, "Posts all pending batched CDR data",
01207 "Usage: cdr submit\n"
01208 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
01209 };
01210
01211 static struct ast_cli_entry cli_status = {
01212 { "cdr", "status", NULL },
01213 handle_cli_status, "Display the CDR status",
01214 "Usage: cdr status\n"
01215 " Displays the Call Detail Record engine system status.\n"
01216 };
01217
01218 static int do_reload(void)
01219 {
01220 struct ast_config *config;
01221 const char *enabled_value;
01222 const char *batched_value;
01223 const char *scheduleronly_value;
01224 const char *batchsafeshutdown_value;
01225 const char *size_value;
01226 const char *time_value;
01227 const char *end_before_h_value;
01228 int cfg_size;
01229 int cfg_time;
01230 int was_enabled;
01231 int was_batchmode;
01232 int res=0;
01233
01234 ast_mutex_lock(&cdr_batch_lock);
01235
01236 batchsize = BATCH_SIZE_DEFAULT;
01237 batchtime = BATCH_TIME_DEFAULT;
01238 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01239 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01240 was_enabled = enabled;
01241 was_batchmode = batchmode;
01242 enabled = 1;
01243 batchmode = 0;
01244
01245
01246 if (cdr_sched > -1)
01247 ast_sched_del(sched, cdr_sched);
01248
01249 if ((config = ast_config_load("cdr.conf"))) {
01250 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01251 enabled = ast_true(enabled_value);
01252 }
01253 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01254 batchmode = ast_true(batched_value);
01255 }
01256 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01257 batchscheduleronly = ast_true(scheduleronly_value);
01258 }
01259 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01260 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01261 }
01262 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01263 if (sscanf(size_value, "%d", &cfg_size) < 1)
01264 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01265 else if (size_value < 0)
01266 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01267 else
01268 batchsize = cfg_size;
01269 }
01270 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01271 if (sscanf(time_value, "%d", &cfg_time) < 1)
01272 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01273 else if (time_value < 0)
01274 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01275 else
01276 batchtime = cfg_time;
01277 }
01278 if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01279 ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01280 }
01281
01282 if (enabled && !batchmode) {
01283 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01284 } else if (enabled && batchmode) {
01285 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01286 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01287 } else {
01288 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01289 }
01290
01291
01292
01293 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01294 ast_cond_init(&cdr_pending_cond, NULL);
01295 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01296 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01297 ast_sched_del(sched, cdr_sched);
01298 } else {
01299 ast_cli_register(&cli_submit);
01300 ast_register_atexit(ast_cdr_engine_term);
01301 res = 0;
01302 }
01303
01304
01305 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01306
01307 pthread_cancel(cdr_thread);
01308 pthread_kill(cdr_thread, SIGURG);
01309 pthread_join(cdr_thread, NULL);
01310 cdr_thread = AST_PTHREADT_NULL;
01311 ast_cond_destroy(&cdr_pending_cond);
01312 ast_cli_unregister(&cli_submit);
01313 ast_unregister_atexit(ast_cdr_engine_term);
01314 res = 0;
01315
01316
01317 if (!batchmode && was_batchmode) {
01318 ast_cdr_engine_term();
01319 }
01320 } else {
01321 res = 0;
01322 }
01323
01324 ast_mutex_unlock(&cdr_batch_lock);
01325 ast_config_destroy(config);
01326
01327 return res;
01328 }
01329
01330 int ast_cdr_engine_init(void)
01331 {
01332 int res;
01333
01334 sched = sched_context_create();
01335 if (!sched) {
01336 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01337 return -1;
01338 }
01339
01340 ast_cli_register(&cli_status);
01341
01342 res = do_reload();
01343 if (res) {
01344 ast_mutex_lock(&cdr_batch_lock);
01345 res = init_batch();
01346 ast_mutex_unlock(&cdr_batch_lock);
01347 }
01348
01349 return res;
01350 }
01351
01352
01353
01354 void ast_cdr_engine_term(void)
01355 {
01356 ast_cdr_submit_batch(batchsafeshutdown);
01357 }
01358
01359 int ast_cdr_engine_reload(void)
01360 {
01361 return do_reload();
01362 }
01363