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