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