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: 118858 $")
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 #define NOAPPLICATION_DEFAULT 1
00091
00092 static int enabled;
00093 static int unanswered;
00094 static int noapplication;
00095 static int batchmode;
00096 static int batchsize;
00097 static int batchtime;
00098 static int batchscheduleronly;
00099 static int batchsafeshutdown;
00100
00101 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
00102
00103
00104 AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
00105 static ast_cond_t cdr_pending_cond;
00106
00107 int check_cdr_enabled()
00108 {
00109 return enabled;
00110 }
00111
00112
00113
00114
00115 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
00116 {
00117 struct ast_cdr_beitem *i;
00118
00119 if (!name)
00120 return -1;
00121 if (!be) {
00122 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00123 return -1;
00124 }
00125
00126 AST_LIST_LOCK(&be_list);
00127 AST_LIST_TRAVERSE(&be_list, i, list) {
00128 if (!strcasecmp(name, i->name))
00129 break;
00130 }
00131 AST_LIST_UNLOCK(&be_list);
00132
00133 if (i) {
00134 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00135 return -1;
00136 }
00137
00138 if (!(i = ast_calloc(1, sizeof(*i))))
00139 return -1;
00140
00141 i->be = be;
00142 ast_copy_string(i->name, name, sizeof(i->name));
00143 ast_copy_string(i->desc, desc, sizeof(i->desc));
00144
00145 AST_LIST_LOCK(&be_list);
00146 AST_LIST_INSERT_HEAD(&be_list, i, list);
00147 AST_LIST_UNLOCK(&be_list);
00148
00149 return 0;
00150 }
00151
00152
00153 void ast_cdr_unregister(const char *name)
00154 {
00155 struct ast_cdr_beitem *i = NULL;
00156
00157 AST_LIST_LOCK(&be_list);
00158 AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
00159 if (!strcasecmp(name, i->name)) {
00160 AST_LIST_REMOVE_CURRENT(&be_list, list);
00161 if (option_verbose > 1)
00162 ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
00163 free(i);
00164 break;
00165 }
00166 }
00167 AST_LIST_TRAVERSE_SAFE_END;
00168 AST_LIST_UNLOCK(&be_list);
00169 }
00170
00171
00172
00173
00174 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
00175 {
00176 struct ast_cdr *newcdr;
00177
00178 if (!cdr)
00179 return NULL;
00180 newcdr = ast_cdr_alloc();
00181 if (!newcdr)
00182 return NULL;
00183
00184 memcpy(newcdr, cdr, sizeof(*newcdr));
00185
00186 memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
00187 ast_cdr_copy_vars(newcdr, cdr);
00188 newcdr->next = NULL;
00189
00190 return newcdr;
00191 }
00192
00193 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
00194 {
00195 if (ast_strlen_zero(name))
00196 return NULL;
00197
00198 for (; cdr; cdr = recur ? cdr->next : NULL) {
00199 struct ast_var_t *variables;
00200 struct varshead *headp = &cdr->varshead;
00201 AST_LIST_TRAVERSE(headp, variables, entries) {
00202 if (!strcasecmp(name, ast_var_name(variables)))
00203 return ast_var_value(variables);
00204 }
00205 }
00206
00207 return NULL;
00208 }
00209
00210 static void cdr_get_tv(struct timeval tv, const char *fmt, char *buf, int bufsize)
00211 {
00212 if (fmt == NULL) {
00213 snprintf(buf, bufsize, "%ld.%06ld", (long)tv.tv_sec, (long)tv.tv_usec);
00214 } else {
00215 time_t t = tv.tv_sec;
00216 if (t) {
00217 struct tm tm;
00218
00219 ast_localtime(&t, &tm, NULL);
00220 strftime(buf, bufsize, fmt, &tm);
00221 }
00222 }
00223 }
00224
00225
00226 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
00227 {
00228 const char *fmt = "%Y-%m-%d %T";
00229 const char *varbuf;
00230
00231 if (!cdr)
00232 return;
00233
00234 *ret = NULL;
00235
00236
00237
00238 if (!strcasecmp(name, "clid"))
00239 ast_copy_string(workspace, cdr->clid, workspacelen);
00240 else if (!strcasecmp(name, "src"))
00241 ast_copy_string(workspace, cdr->src, workspacelen);
00242 else if (!strcasecmp(name, "dst"))
00243 ast_copy_string(workspace, cdr->dst, workspacelen);
00244 else if (!strcasecmp(name, "dcontext"))
00245 ast_copy_string(workspace, cdr->dcontext, workspacelen);
00246 else if (!strcasecmp(name, "channel"))
00247 ast_copy_string(workspace, cdr->channel, workspacelen);
00248 else if (!strcasecmp(name, "dstchannel"))
00249 ast_copy_string(workspace, cdr->dstchannel, workspacelen);
00250 else if (!strcasecmp(name, "lastapp"))
00251 ast_copy_string(workspace, cdr->lastapp, workspacelen);
00252 else if (!strcasecmp(name, "lastdata"))
00253 ast_copy_string(workspace, cdr->lastdata, workspacelen);
00254 else if (!strcasecmp(name, "start"))
00255 cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
00256 else if (!strcasecmp(name, "answer"))
00257 cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
00258 else if (!strcasecmp(name, "end"))
00259 cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
00260 else if (!strcasecmp(name, "duration"))
00261 snprintf(workspace, workspacelen, "%ld", cdr->duration);
00262 else if (!strcasecmp(name, "billsec"))
00263 snprintf(workspace, workspacelen, "%ld", cdr->billsec);
00264 else if (!strcasecmp(name, "disposition")) {
00265 if (raw) {
00266 snprintf(workspace, workspacelen, "%ld", cdr->disposition);
00267 } else {
00268 ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
00269 }
00270 } else if (!strcasecmp(name, "amaflags")) {
00271 if (raw) {
00272 snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
00273 } else {
00274 ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
00275 }
00276 } else if (!strcasecmp(name, "accountcode"))
00277 ast_copy_string(workspace, cdr->accountcode, workspacelen);
00278 else if (!strcasecmp(name, "uniqueid"))
00279 ast_copy_string(workspace, cdr->uniqueid, workspacelen);
00280 else if (!strcasecmp(name, "userfield"))
00281 ast_copy_string(workspace, cdr->userfield, workspacelen);
00282 else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
00283 ast_copy_string(workspace, varbuf, workspacelen);
00284 else
00285 workspace[0] = '\0';
00286
00287 if (!ast_strlen_zero(workspace))
00288 *ret = workspace;
00289 }
00290
00291
00292 static const char *cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
00293 "lastapp", "lastdata", "start", "answer", "end", "duration",
00294 "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
00295 "userfield", NULL };
00296
00297
00298
00299 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
00300 {
00301 struct ast_var_t *newvariable;
00302 struct varshead *headp;
00303 int x;
00304
00305 if (!cdr)
00306 return -1;
00307
00308 for(x = 0; cdr_readonly_vars[x]; x++) {
00309 if (!strcasecmp(name, cdr_readonly_vars[x])) {
00310 ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
00311 return -1;
00312 }
00313 }
00314
00315 if (!cdr) {
00316 ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
00317 return -1;
00318 }
00319
00320 for (; cdr; cdr = recur ? cdr->next : NULL) {
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 return 0;
00339 }
00340
00341 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
00342 {
00343 struct ast_var_t *variables, *newvariable = NULL;
00344 struct varshead *headpa, *headpb;
00345 const char *var, *val;
00346 int x = 0;
00347
00348 if (!to_cdr || !from_cdr)
00349 return 0;
00350
00351 headpa = &from_cdr->varshead;
00352 headpb = &to_cdr->varshead;
00353
00354 AST_LIST_TRAVERSE(headpa,variables,entries) {
00355 if (variables &&
00356 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00357 !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00358 newvariable = ast_var_assign(var, val);
00359 AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
00360 x++;
00361 }
00362 }
00363
00364 return x;
00365 }
00366
00367 int ast_cdr_serialize_variables(struct ast_cdr *cdr, char *buf, size_t size, char delim, char sep, int recur)
00368 {
00369 struct ast_var_t *variables;
00370 const char *var, *val;
00371 char *tmp;
00372 char workspace[256];
00373 int total = 0, x = 0, i;
00374
00375 memset(buf, 0, size);
00376
00377 for (; cdr; cdr = recur ? cdr->next : NULL) {
00378 if (++x > 1)
00379 ast_build_string(&buf, &size, "\n");
00380
00381 AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
00382 if (variables &&
00383 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00384 !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00385 if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, var, delim, val, sep)) {
00386 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00387 break;
00388 } else
00389 total++;
00390 } else
00391 break;
00392 }
00393
00394 for (i = 0; cdr_readonly_vars[i]; i++) {
00395 workspace[0] = 0;
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 check_post(cdr);
00705 if (cdr->disposition < AST_CDR_ANSWERED)
00706 cdr->disposition = AST_CDR_ANSWERED;
00707 if (ast_tvzero(cdr->answer))
00708 cdr->answer = ast_tvnow();
00709 }
00710 }
00711
00712 void ast_cdr_busy(struct ast_cdr *cdr)
00713 {
00714
00715 for (; cdr; cdr = cdr->next) {
00716 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00717 check_post(cdr);
00718 if (cdr->disposition < AST_CDR_BUSY)
00719 cdr->disposition = AST_CDR_BUSY;
00720 }
00721 }
00722 }
00723
00724 void ast_cdr_failed(struct ast_cdr *cdr)
00725 {
00726 for (; cdr; cdr = cdr->next) {
00727 check_post(cdr);
00728 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00729 if (cdr->disposition < AST_CDR_FAILED)
00730 cdr->disposition = AST_CDR_FAILED;
00731 }
00732 }
00733 }
00734
00735 void ast_cdr_noanswer(struct ast_cdr *cdr)
00736 {
00737 char *chan;
00738
00739 while (cdr) {
00740 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00741 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00742 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00743 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00744 if (cdr->disposition < AST_CDR_NOANSWER)
00745 cdr->disposition = AST_CDR_NOANSWER;
00746 }
00747 cdr = cdr->next;
00748 }
00749 }
00750
00751
00752
00753
00754 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00755 {
00756 int res = 0;
00757
00758 for (; cdr; cdr = cdr->next) {
00759 switch(cause) {
00760
00761 case AST_CAUSE_BUSY:
00762 ast_cdr_busy(cdr);
00763 break;
00764 case AST_CAUSE_NORMAL:
00765 break;
00766 default:
00767 res = -1;
00768 }
00769 }
00770 return res;
00771 }
00772
00773 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
00774 {
00775 for (; cdr; cdr = cdr->next) {
00776 check_post(cdr);
00777 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00778 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00779 }
00780 }
00781
00782 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
00783 {
00784
00785 for (; cdr; cdr = cdr->next) {
00786 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00787 check_post(cdr);
00788 ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
00789 ast_copy_string(cdr->lastdata, S_OR(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 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00831 set_one_cid(cdr, c);
00832
00833 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL;
00834 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
00835 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00836
00837 ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
00838 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
00839
00840 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00841 }
00842 }
00843 return 0;
00844 }
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858 void ast_cdr_end(struct ast_cdr *cdr)
00859 {
00860 for ( ; cdr ; cdr = cdr->next) {
00861 check_post(cdr);
00862 if (ast_tvzero(cdr->end))
00863 cdr->end = ast_tvnow();
00864 if (ast_tvzero(cdr->start)) {
00865 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00866 cdr->disposition = AST_CDR_FAILED;
00867 } else
00868 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00869 cdr->billsec = ast_tvzero(cdr->answer) ? 0 : cdr->end.tv_sec - cdr->answer.tv_sec;
00870 }
00871 }
00872
00873 char *ast_cdr_disp2str(int disposition)
00874 {
00875 switch (disposition) {
00876 case AST_CDR_NULL:
00877 return "NO ANSWER";
00878 case AST_CDR_NOANSWER:
00879 return "NO ANSWER";
00880 case AST_CDR_FAILED:
00881 return "FAILED";
00882 case AST_CDR_BUSY:
00883 return "BUSY";
00884 case AST_CDR_ANSWERED:
00885 return "ANSWERED";
00886 }
00887 return "UNKNOWN";
00888 }
00889
00890
00891 char *ast_cdr_flags2str(int flag)
00892 {
00893 switch(flag) {
00894 case AST_CDR_OMIT:
00895 return "OMIT";
00896 case AST_CDR_BILLING:
00897 return "BILLING";
00898 case AST_CDR_DOCUMENTATION:
00899 return "DOCUMENTATION";
00900 }
00901 return "Unknown";
00902 }
00903
00904 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00905 {
00906 struct ast_cdr *cdr = chan->cdr;
00907
00908 ast_string_field_set(chan, accountcode, account);
00909 for ( ; cdr ; cdr = cdr->next) {
00910 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00911 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
00912 }
00913 }
00914 return 0;
00915 }
00916
00917 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
00918 {
00919 struct ast_cdr *cdr;
00920 int newflag = ast_cdr_amaflags2int(flag);
00921 if (newflag) {
00922 for (cdr = chan->cdr; cdr; cdr = cdr->next) {
00923 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00924 cdr->amaflags = newflag;
00925 }
00926 }
00927 }
00928
00929 return 0;
00930 }
00931
00932 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
00933 {
00934 struct ast_cdr *cdr = chan->cdr;
00935
00936 for ( ; cdr ; cdr = cdr->next) {
00937 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00938 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
00939 }
00940
00941 return 0;
00942 }
00943
00944 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
00945 {
00946 struct ast_cdr *cdr = chan->cdr;
00947
00948 for ( ; cdr ; cdr = cdr->next) {
00949 int len = strlen(cdr->userfield);
00950
00951 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00952 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
00953 }
00954
00955 return 0;
00956 }
00957
00958 int ast_cdr_update(struct ast_channel *c)
00959 {
00960 struct ast_cdr *cdr = c->cdr;
00961
00962 for ( ; cdr ; cdr = cdr->next) {
00963 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00964 set_one_cid(cdr, c);
00965
00966
00967 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00968
00969
00970 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
00971 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
00972 }
00973 }
00974
00975 return 0;
00976 }
00977
00978 int ast_cdr_amaflags2int(const char *flag)
00979 {
00980 if (!strcasecmp(flag, "default"))
00981 return 0;
00982 if (!strcasecmp(flag, "omit"))
00983 return AST_CDR_OMIT;
00984 if (!strcasecmp(flag, "billing"))
00985 return AST_CDR_BILLING;
00986 if (!strcasecmp(flag, "documentation"))
00987 return AST_CDR_DOCUMENTATION;
00988 return -1;
00989 }
00990
00991 static void post_cdr(struct ast_cdr *cdr)
00992 {
00993 char *chan;
00994 struct ast_cdr_beitem *i;
00995
00996 for ( ; cdr ; cdr = cdr->next) {
00997 if ( (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) ||
00998 (unanswered && !noapplication && ast_strlen_zero(cdr->lastapp)) ) {
00999
01000 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01001 continue;
01002 }
01003
01004 chan = S_OR(cdr->channel, "<unknown>");
01005 check_post(cdr);
01006 if (ast_tvzero(cdr->end))
01007 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
01008 if (ast_tvzero(cdr->start))
01009 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
01010 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01011 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01012 continue;
01013 AST_LIST_LOCK(&be_list);
01014 AST_LIST_TRAVERSE(&be_list, i, list) {
01015 i->be(cdr);
01016 }
01017 AST_LIST_UNLOCK(&be_list);
01018 }
01019 }
01020
01021 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01022 {
01023 struct ast_cdr *dup;
01024 struct ast_flags flags = { 0 };
01025
01026 if (_flags)
01027 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01028
01029 for ( ; cdr ; cdr = cdr->next) {
01030
01031 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01032 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01033 ast_cdr_end(cdr);
01034 if ((dup = ast_cdr_dup(cdr))) {
01035 ast_cdr_detach(dup);
01036 }
01037 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01038 }
01039
01040
01041 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01042 ast_cdr_free_vars(cdr, 0);
01043 }
01044
01045
01046 ast_clear_flag(cdr, AST_FLAGS_ALL);
01047 memset(&cdr->start, 0, sizeof(cdr->start));
01048 memset(&cdr->end, 0, sizeof(cdr->end));
01049 memset(&cdr->answer, 0, sizeof(cdr->answer));
01050 cdr->billsec = 0;
01051 cdr->duration = 0;
01052 ast_cdr_start(cdr);
01053 cdr->disposition = AST_CDR_NULL;
01054 }
01055 }
01056 }
01057
01058 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01059 {
01060 struct ast_cdr *ret;
01061
01062 if (cdr) {
01063 ret = cdr;
01064
01065 while (cdr->next)
01066 cdr = cdr->next;
01067 cdr->next = newcdr;
01068 } else {
01069 ret = newcdr;
01070 }
01071
01072 return ret;
01073 }
01074
01075
01076 static void reset_batch(void)
01077 {
01078 batch->size = 0;
01079 batch->head = NULL;
01080 batch->tail = NULL;
01081 }
01082
01083
01084 static int init_batch(void)
01085 {
01086
01087 if (!(batch = ast_malloc(sizeof(*batch))))
01088 return -1;
01089
01090 reset_batch();
01091
01092 return 0;
01093 }
01094
01095 static void *do_batch_backend_process(void *data)
01096 {
01097 struct ast_cdr_batch_item *processeditem;
01098 struct ast_cdr_batch_item *batchitem = data;
01099
01100
01101 while (batchitem) {
01102 post_cdr(batchitem->cdr);
01103 ast_cdr_free(batchitem->cdr);
01104 processeditem = batchitem;
01105 batchitem = batchitem->next;
01106 free(processeditem);
01107 }
01108
01109 return NULL;
01110 }
01111
01112 void ast_cdr_submit_batch(int shutdown)
01113 {
01114 struct ast_cdr_batch_item *oldbatchitems = NULL;
01115 pthread_attr_t attr;
01116 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01117
01118
01119 if (!batch || !batch->head)
01120 return;
01121
01122
01123 ast_mutex_lock(&cdr_batch_lock);
01124 oldbatchitems = batch->head;
01125 reset_batch();
01126 ast_mutex_unlock(&cdr_batch_lock);
01127
01128
01129
01130 if (batchscheduleronly || shutdown) {
01131 if (option_debug)
01132 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
01133 do_batch_backend_process(oldbatchitems);
01134 } else {
01135 pthread_attr_init(&attr);
01136 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01137 if (ast_pthread_create_background(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
01138 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01139 do_batch_backend_process(oldbatchitems);
01140 } else {
01141 if (option_debug)
01142 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
01143 }
01144 pthread_attr_destroy(&attr);
01145 }
01146 }
01147
01148 static int submit_scheduled_batch(const void *data)
01149 {
01150 ast_cdr_submit_batch(0);
01151
01152 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01153
01154 return 0;
01155 }
01156
01157 static void submit_unscheduled_batch(void)
01158 {
01159
01160 AST_SCHED_DEL(sched, cdr_sched);
01161
01162 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01163
01164 ast_mutex_lock(&cdr_pending_lock);
01165 ast_cond_signal(&cdr_pending_cond);
01166 ast_mutex_unlock(&cdr_pending_lock);
01167 }
01168
01169 void ast_cdr_detach(struct ast_cdr *cdr)
01170 {
01171 struct ast_cdr_batch_item *newtail;
01172 int curr;
01173
01174 if (!cdr)
01175 return;
01176
01177
01178 if (!enabled) {
01179 if (option_debug)
01180 ast_log(LOG_DEBUG, "Dropping CDR !\n");
01181 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01182 ast_cdr_free(cdr);
01183 return;
01184 }
01185
01186
01187 if (!batchmode) {
01188 post_cdr(cdr);
01189 ast_cdr_free(cdr);
01190 return;
01191 }
01192
01193
01194 if (option_debug)
01195 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
01196
01197
01198 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01199 post_cdr(cdr);
01200 ast_cdr_free(cdr);
01201 return;
01202 }
01203
01204
01205 ast_mutex_lock(&cdr_batch_lock);
01206 if (!batch)
01207 init_batch();
01208 if (!batch->head) {
01209
01210 batch->head = newtail;
01211 } else {
01212
01213 batch->tail->next = newtail;
01214 }
01215 newtail->cdr = cdr;
01216 batch->tail = newtail;
01217 curr = batch->size++;
01218 ast_mutex_unlock(&cdr_batch_lock);
01219
01220
01221 if (curr >= (batchsize - 1))
01222 submit_unscheduled_batch();
01223 }
01224
01225 static void *do_cdr(void *data)
01226 {
01227 struct timespec timeout;
01228 int schedms;
01229 int numevents = 0;
01230
01231 for(;;) {
01232 struct timeval now;
01233 schedms = ast_sched_wait(sched);
01234
01235 if (schedms <= 0)
01236 schedms = 1000;
01237 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01238 timeout.tv_sec = now.tv_sec;
01239 timeout.tv_nsec = now.tv_usec * 1000;
01240
01241 ast_mutex_lock(&cdr_pending_lock);
01242 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01243 numevents = ast_sched_runq(sched);
01244 ast_mutex_unlock(&cdr_pending_lock);
01245 if (option_debug > 1)
01246 ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01247 }
01248
01249 return NULL;
01250 }
01251
01252 static int handle_cli_status(int fd, int argc, char *argv[])
01253 {
01254 struct ast_cdr_beitem *beitem=NULL;
01255 int cnt=0;
01256 long nextbatchtime=0;
01257
01258 if (argc > 2)
01259 return RESULT_SHOWUSAGE;
01260
01261 ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
01262 ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
01263 if (enabled) {
01264 ast_cli(fd, "CDR output unanswered calls: %s\n", unanswered ? "yes" : "no");
01265 if (unanswered) {
01266 ast_cli(fd, "CDR output no application calls: %s\n", noapplication ? "yes" : "no");
01267 }
01268 if (batchmode) {
01269 if (batch)
01270 cnt = batch->size;
01271 if (cdr_sched > -1)
01272 nextbatchtime = ast_sched_when(sched, cdr_sched);
01273 ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
01274 ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
01275 ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
01276 ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
01277 ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
01278 ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
01279 }
01280 AST_LIST_LOCK(&be_list);
01281 AST_LIST_TRAVERSE(&be_list, beitem, list) {
01282 ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
01283 }
01284 AST_LIST_UNLOCK(&be_list);
01285 }
01286
01287 return 0;
01288 }
01289
01290 static int handle_cli_submit(int fd, int argc, char *argv[])
01291 {
01292 if (argc > 2)
01293 return RESULT_SHOWUSAGE;
01294
01295 submit_unscheduled_batch();
01296 ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01297
01298 return 0;
01299 }
01300
01301 static struct ast_cli_entry cli_submit = {
01302 { "cdr", "submit", NULL },
01303 handle_cli_submit, "Posts all pending batched CDR data",
01304 "Usage: cdr submit\n"
01305 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
01306 };
01307
01308 static struct ast_cli_entry cli_status = {
01309 { "cdr", "status", NULL },
01310 handle_cli_status, "Display the CDR status",
01311 "Usage: cdr status\n"
01312 " Displays the Call Detail Record engine system status.\n"
01313 };
01314
01315 static int do_reload(void)
01316 {
01317 struct ast_config *config;
01318 const char *enabled_value;
01319 const char *unanswered_value;
01320 const char *noapplication_value;
01321 const char *batched_value;
01322 const char *scheduleronly_value;
01323 const char *batchsafeshutdown_value;
01324 const char *size_value;
01325 const char *time_value;
01326 const char *end_before_h_value;
01327 int cfg_size;
01328 int cfg_time;
01329 int was_enabled;
01330 int was_batchmode;
01331 int res=0;
01332
01333 ast_mutex_lock(&cdr_batch_lock);
01334
01335 noapplication = NOAPPLICATION_DEFAULT;
01336 batchsize = BATCH_SIZE_DEFAULT;
01337 batchtime = BATCH_TIME_DEFAULT;
01338 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01339 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01340 was_enabled = enabled;
01341 was_batchmode = batchmode;
01342 enabled = 1;
01343 batchmode = 0;
01344
01345
01346 AST_SCHED_DEL(sched, cdr_sched);
01347
01348 if ((config = ast_config_load("cdr.conf"))) {
01349 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01350 enabled = ast_true(enabled_value);
01351 }
01352 if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
01353 unanswered = ast_true(unanswered_value);
01354 if (unanswered)
01355 if ((noapplication_value = ast_variable_retrieve(config, "general", "noapplication"))) {
01356 noapplication = ast_true(noapplication_value);
01357 }
01358 }
01359 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01360 batchmode = ast_true(batched_value);
01361 }
01362 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01363 batchscheduleronly = ast_true(scheduleronly_value);
01364 }
01365 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01366 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01367 }
01368 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01369 if (sscanf(size_value, "%d", &cfg_size) < 1)
01370 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01371 else if (size_value < 0)
01372 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01373 else
01374 batchsize = cfg_size;
01375 }
01376 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01377 if (sscanf(time_value, "%d", &cfg_time) < 1)
01378 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01379 else if (time_value < 0)
01380 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01381 else
01382 batchtime = cfg_time;
01383 }
01384 if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01385 ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01386 }
01387
01388 if (enabled && !batchmode) {
01389 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01390 } else if (enabled && batchmode) {
01391 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01392 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01393 } else {
01394 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01395 }
01396
01397
01398
01399 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01400 ast_cond_init(&cdr_pending_cond, NULL);
01401 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01402 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01403 AST_SCHED_DEL(sched, cdr_sched);
01404 } else {
01405 ast_cli_register(&cli_submit);
01406 ast_register_atexit(ast_cdr_engine_term);
01407 res = 0;
01408 }
01409
01410
01411 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01412
01413 pthread_cancel(cdr_thread);
01414 pthread_kill(cdr_thread, SIGURG);
01415 pthread_join(cdr_thread, NULL);
01416 cdr_thread = AST_PTHREADT_NULL;
01417 ast_cond_destroy(&cdr_pending_cond);
01418 ast_cli_unregister(&cli_submit);
01419 ast_unregister_atexit(ast_cdr_engine_term);
01420 res = 0;
01421
01422
01423 if (!batchmode && was_batchmode) {
01424 ast_cdr_engine_term();
01425 }
01426 } else {
01427 res = 0;
01428 }
01429
01430 ast_mutex_unlock(&cdr_batch_lock);
01431 ast_config_destroy(config);
01432
01433 return res;
01434 }
01435
01436 int ast_cdr_engine_init(void)
01437 {
01438 int res;
01439
01440 sched = sched_context_create();
01441 if (!sched) {
01442 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01443 return -1;
01444 }
01445
01446 ast_cli_register(&cli_status);
01447
01448 res = do_reload();
01449 if (res) {
01450 ast_mutex_lock(&cdr_batch_lock);
01451 res = init_batch();
01452 ast_mutex_unlock(&cdr_batch_lock);
01453 }
01454
01455 return res;
01456 }
01457
01458
01459
01460 void ast_cdr_engine_term(void)
01461 {
01462 ast_cdr_submit_batch(batchsafeshutdown);
01463 }
01464
01465 int ast_cdr_engine_reload(void)
01466 {
01467 return do_reload();
01468 }
01469