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