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: 287114 $")
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
00107
00108
00109 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
00110 {
00111 struct ast_cdr_beitem *i;
00112
00113 if (!name)
00114 return -1;
00115 if (!be) {
00116 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00117 return -1;
00118 }
00119
00120 AST_LIST_LOCK(&be_list);
00121 AST_LIST_TRAVERSE(&be_list, i, list) {
00122 if (!strcasecmp(name, i->name))
00123 break;
00124 }
00125 AST_LIST_UNLOCK(&be_list);
00126
00127 if (i) {
00128 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00129 return -1;
00130 }
00131
00132 if (!(i = ast_calloc(1, sizeof(*i))))
00133 return -1;
00134
00135 i->be = be;
00136 ast_copy_string(i->name, name, sizeof(i->name));
00137 ast_copy_string(i->desc, desc, sizeof(i->desc));
00138
00139 AST_LIST_LOCK(&be_list);
00140 AST_LIST_INSERT_HEAD(&be_list, i, list);
00141 AST_LIST_UNLOCK(&be_list);
00142
00143 return 0;
00144 }
00145
00146
00147 void ast_cdr_unregister(const char *name)
00148 {
00149 struct ast_cdr_beitem *i = NULL;
00150
00151 AST_LIST_LOCK(&be_list);
00152 AST_LIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
00153 if (!strcasecmp(name, i->name)) {
00154 AST_LIST_REMOVE_CURRENT(&be_list, list);
00155 if (option_verbose > 1)
00156 ast_verbose(VERBOSE_PREFIX_2 "Unregistered '%s' CDR backend\n", name);
00157 free(i);
00158 break;
00159 }
00160 }
00161 AST_LIST_TRAVERSE_SAFE_END;
00162 AST_LIST_UNLOCK(&be_list);
00163 }
00164
00165 int ast_cdr_isset_unanswered(void)
00166 {
00167 return unanswered;
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_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00321 continue;
00322 headp = &cdr->varshead;
00323 AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
00324 if (!strcasecmp(ast_var_name(newvariable), name)) {
00325
00326 AST_LIST_REMOVE_CURRENT(headp, entries);
00327 ast_var_delete(newvariable);
00328 break;
00329 }
00330 }
00331 AST_LIST_TRAVERSE_SAFE_END;
00332
00333 if (value) {
00334 newvariable = ast_var_assign(name, value);
00335 AST_LIST_INSERT_HEAD(headp, newvariable, entries);
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;
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 (!(var = ast_var_name(variables))) {
00384 continue;
00385 }
00386
00387 if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, var, delim, S_OR(ast_var_value(variables), ""), sep)) {
00388 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00389 break;
00390 }
00391
00392 total++;
00393 }
00394
00395 for (i = 0; cdr_readonly_vars[i]; i++) {
00396 workspace[0] = 0;
00397 ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
00398 if (!tmp)
00399 continue;
00400
00401 if (ast_build_string(&buf, &size, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep)) {
00402 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00403 break;
00404 } else
00405 total++;
00406 }
00407 }
00408
00409 return total;
00410 }
00411
00412
00413 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
00414 {
00415
00416
00417 for (; cdr; cdr = recur ? cdr->next : NULL) {
00418 struct ast_var_t *vardata;
00419 struct varshead *headp = &cdr->varshead;
00420 while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
00421 ast_var_delete(vardata);
00422 }
00423 }
00424
00425
00426 static void check_post(struct ast_cdr *cdr)
00427 {
00428 if (!cdr)
00429 return;
00430 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00431 ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
00432 }
00433
00434 void ast_cdr_free(struct ast_cdr *cdr)
00435 {
00436
00437 while (cdr) {
00438 struct ast_cdr *next = cdr->next;
00439 char *chan = S_OR(cdr->channel, "<unknown>");
00440 if (option_verbose > 1 && !ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
00441 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' not posted\n", chan);
00442 if (option_verbose > 1 && ast_tvzero(cdr->end))
00443 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
00444 if (option_verbose > 1 && ast_tvzero(cdr->start))
00445 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks start\n", chan);
00446
00447 ast_cdr_free_vars(cdr, 0);
00448 free(cdr);
00449 cdr = next;
00450 }
00451 }
00452
00453
00454 void ast_cdr_discard(struct ast_cdr *cdr)
00455 {
00456 while (cdr) {
00457 struct ast_cdr *next = cdr->next;
00458
00459 ast_cdr_free_vars(cdr, 0);
00460 free(cdr);
00461 cdr = next;
00462 }
00463 }
00464
00465 struct ast_cdr *ast_cdr_alloc(void)
00466 {
00467 struct ast_cdr *x = ast_calloc(1, sizeof(struct ast_cdr));
00468 if (!x)
00469 ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
00470 return x;
00471 }
00472
00473 static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
00474 {
00475 struct ast_var_t *variablesfrom,*variablesto;
00476 struct varshead *headpfrom = &to->varshead;
00477 struct varshead *headpto = &from->varshead;
00478 AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
00479
00480 const char *fromvarname = NULL, *fromvarval = NULL;
00481 const char *tovarname = NULL, *tovarval = NULL;
00482 fromvarname = ast_var_name(variablesfrom);
00483 fromvarval = ast_var_value(variablesfrom);
00484 tovarname = 0;
00485
00486
00487 AST_LIST_TRAVERSE(headpto, variablesto, entries) {
00488
00489
00490 if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
00491 tovarname = ast_var_name(variablesto);
00492 tovarval = ast_var_value(variablesto);
00493 break;
00494 }
00495 }
00496 if (tovarname && strcasecmp(fromvarval,tovarval) != 0) {
00497 ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
00498 continue;
00499 } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0)
00500 continue;
00501
00502
00503 AST_LIST_REMOVE_CURRENT(headpfrom, entries);
00504 AST_LIST_INSERT_HEAD(headpto, variablesfrom, entries);
00505 }
00506 AST_LIST_TRAVERSE_SAFE_END;
00507 }
00508
00509 void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
00510 {
00511 struct ast_cdr *zcdr;
00512 struct ast_cdr *lto = NULL;
00513 struct ast_cdr *lfrom = NULL;
00514 int discard_from = 0;
00515
00516 if (!to || !from)
00517 return;
00518
00519
00520 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00521 zcdr = to;
00522 while (to->next) {
00523 lto = to;
00524 to = to->next;
00525 }
00526
00527 if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00528 ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
00529 to = zcdr;
00530 lto = NULL;
00531 }
00532 }
00533
00534 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
00535 discard_from = 1;
00536 if (lto) {
00537 struct ast_cdr *llfrom = NULL;
00538
00539 lto->next = from;
00540 lfrom = from;
00541 while (lfrom && lfrom->next) {
00542 if (!lfrom->next->next)
00543 llfrom = lfrom;
00544 lfrom = lfrom->next;
00545 }
00546
00547 llfrom->next = to;
00548 from = lfrom;
00549 } else {
00550
00551 struct ast_cdr tcdr;
00552 struct ast_cdr *llfrom = NULL;
00553 memcpy(&tcdr, to, sizeof(tcdr));
00554
00555 memcpy(to, from, sizeof(*to));
00556 lfrom = from;
00557 while (lfrom && lfrom->next) {
00558 if (!lfrom->next->next)
00559 llfrom = lfrom;
00560 lfrom = lfrom->next;
00561 }
00562 from->next = NULL;
00563
00564 if (llfrom == from)
00565 to = to->next = ast_cdr_dup(&tcdr);
00566 else
00567 to = llfrom->next = ast_cdr_dup(&tcdr);
00568 from = lfrom;
00569 }
00570 }
00571
00572 if (!ast_tvzero(from->start)) {
00573 if (!ast_tvzero(to->start)) {
00574 if (ast_tvcmp(to->start, from->start) > 0 ) {
00575 to->start = from->start;
00576 from->start = ast_tv(0,0);
00577 }
00578
00579 } else {
00580 to->start = from->start;
00581 from->start = ast_tv(0,0);
00582 }
00583 }
00584 if (!ast_tvzero(from->answer)) {
00585 if (!ast_tvzero(to->answer)) {
00586 if (ast_tvcmp(to->answer, from->answer) > 0 ) {
00587 to->answer = from->answer;
00588 from->answer = ast_tv(0,0);
00589 }
00590
00591 } else {
00592 to->answer = from->answer;
00593 from->answer = ast_tv(0,0);
00594 }
00595 }
00596 if (!ast_tvzero(from->end)) {
00597 if (!ast_tvzero(to->end)) {
00598 if (ast_tvcmp(to->end, from->end) < 0 ) {
00599 to->end = from->end;
00600 from->end = ast_tv(0,0);
00601 to->duration = to->end.tv_sec - to->start.tv_sec;
00602 to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00603 }
00604
00605 } else {
00606 to->end = from->end;
00607 from->end = ast_tv(0,0);
00608 to->duration = to->end.tv_sec - to->start.tv_sec;
00609 to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00610 }
00611 }
00612 if (to->disposition < from->disposition) {
00613 to->disposition = from->disposition;
00614 from->disposition = AST_CDR_NOANSWER;
00615 }
00616 if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
00617 ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
00618 from->lastapp[0] = 0;
00619 }
00620 if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
00621 ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
00622 from->lastdata[0] = 0;
00623 }
00624 if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
00625 ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
00626 from->dcontext[0] = 0;
00627 }
00628 if (ast_strlen_zero(to->dstchannel) && !ast_strlen_zero(from->dstchannel)) {
00629 ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
00630 from->dstchannel[0] = 0;
00631 }
00632 if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
00633 ast_copy_string(to->channel, from->channel, sizeof(to->channel));
00634 from->channel[0] = 0;
00635 }
00636 if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
00637 ast_copy_string(to->src, from->src, sizeof(to->src));
00638 from->src[0] = 0;
00639 }
00640 if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
00641 ast_copy_string(to->clid, from->clid, sizeof(to->clid));
00642 from->clid[0] = 0;
00643 }
00644 if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
00645 ast_copy_string(to->dst, from->dst, sizeof(to->dst));
00646 from->dst[0] = 0;
00647 }
00648 if (!to->amaflags)
00649 to->amaflags = AST_CDR_DOCUMENTATION;
00650 if (!from->amaflags)
00651 from->amaflags = AST_CDR_DOCUMENTATION;
00652 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (to->amaflags == AST_CDR_DOCUMENTATION && from->amaflags != AST_CDR_DOCUMENTATION)) {
00653 to->amaflags = from->amaflags;
00654 }
00655 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
00656 ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
00657 }
00658 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
00659 ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
00660 }
00661
00662 cdr_merge_vars(from, to);
00663
00664 if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
00665 ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
00666 if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
00667 ast_set_flag(to, AST_CDR_FLAG_POSTED);
00668 if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
00669 ast_set_flag(to, AST_CDR_FLAG_LOCKED);
00670 if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
00671 ast_set_flag(to, AST_CDR_FLAG_CHILD);
00672 if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
00673 ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
00674
00675
00676 while (from->next) {
00677
00678 zcdr = from->next;
00679 from->next = zcdr->next;
00680 zcdr->next = NULL;
00681
00682 ast_cdr_append(to, zcdr);
00683 }
00684 if (discard_from)
00685 ast_cdr_discard(from);
00686 }
00687
00688 void ast_cdr_start(struct ast_cdr *cdr)
00689 {
00690 char *chan;
00691
00692 for (; cdr; cdr = cdr->next) {
00693 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00694 chan = S_OR(cdr->channel, "<unknown>");
00695 check_post(cdr);
00696 cdr->start = ast_tvnow();
00697 }
00698 }
00699 }
00700
00701 void ast_cdr_answer(struct ast_cdr *cdr)
00702 {
00703
00704 for (; cdr; cdr = cdr->next) {
00705 if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00706 continue;
00707 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00708 continue;
00709 check_post(cdr);
00710 if (cdr->disposition < AST_CDR_ANSWERED)
00711 cdr->disposition = AST_CDR_ANSWERED;
00712 if (ast_tvzero(cdr->answer))
00713 cdr->answer = ast_tvnow();
00714 }
00715 }
00716
00717 void ast_cdr_busy(struct ast_cdr *cdr)
00718 {
00719
00720 for (; cdr; cdr = cdr->next) {
00721 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00722 check_post(cdr);
00723 cdr->disposition = AST_CDR_BUSY;
00724 }
00725 }
00726 }
00727
00728 void ast_cdr_failed(struct ast_cdr *cdr)
00729 {
00730 for (; cdr; cdr = cdr->next) {
00731 check_post(cdr);
00732 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00733 if (cdr->disposition < AST_CDR_FAILED)
00734 cdr->disposition = AST_CDR_FAILED;
00735 }
00736 }
00737 }
00738
00739 void ast_cdr_noanswer(struct ast_cdr *cdr)
00740 {
00741 char *chan;
00742
00743 while (cdr) {
00744 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00745 check_post(cdr);
00746 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
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_NO_ANSWER:
00767 ast_cdr_noanswer(cdr);
00768 break;
00769 case AST_CAUSE_NORMAL:
00770 break;
00771 default:
00772 res = -1;
00773 }
00774 }
00775 return res;
00776 }
00777
00778 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
00779 {
00780 for (; cdr; cdr = cdr->next) {
00781 check_post(cdr);
00782 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00783 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00784 }
00785 }
00786
00787 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
00788 {
00789
00790 for (; cdr; cdr = cdr->next) {
00791 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00792 check_post(cdr);
00793 ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
00794 ast_copy_string(cdr->lastdata, S_OR(data, ""), sizeof(cdr->lastdata));
00795 }
00796 }
00797 }
00798
00799 void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t)
00800 {
00801
00802 for (; cdr; cdr = cdr->next) {
00803 if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00804 continue;
00805 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00806 continue;
00807 check_post(cdr);
00808 cdr->answer = t;
00809 }
00810 }
00811
00812 void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition)
00813 {
00814
00815 for (; cdr; cdr = cdr->next) {
00816 if (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00817 continue;
00818 check_post(cdr);
00819 cdr->disposition = disposition;
00820 }
00821 }
00822
00823
00824 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
00825 {
00826
00827 const char *num = S_OR(c->cid.cid_ani, c->cid.cid_num);
00828 if (!cdr)
00829 return;
00830 if (!ast_strlen_zero(c->cid.cid_name)) {
00831 if (!ast_strlen_zero(num))
00832 snprintf(cdr->clid, sizeof(cdr->clid), "\"%s\" <%s>", c->cid.cid_name, num);
00833 else
00834 ast_copy_string(cdr->clid, c->cid.cid_name, sizeof(cdr->clid));
00835 } else if (!ast_strlen_zero(num)) {
00836 ast_copy_string(cdr->clid, num, sizeof(cdr->clid));
00837 } else {
00838 cdr->clid[0] = '\0';
00839 }
00840 ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
00841 ast_cdr_setvar(cdr, "dnid", S_OR(c->cid.cid_dnid, ""), 0);
00842
00843 }
00844 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00845 {
00846 for (; cdr; cdr = cdr->next) {
00847 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00848 set_one_cid(cdr, c);
00849 }
00850 return 0;
00851 }
00852
00853 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00854 {
00855 char *chan;
00856
00857 for ( ; cdr ; cdr = cdr->next) {
00858 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00859 chan = S_OR(cdr->channel, "<unknown>");
00860 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00861 set_one_cid(cdr, c);
00862
00863 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER;
00864 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
00865 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00866
00867 ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
00868 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
00869
00870 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00871 }
00872 }
00873 return 0;
00874 }
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888 void ast_cdr_end(struct ast_cdr *cdr)
00889 {
00890 for ( ; cdr ; cdr = cdr->next) {
00891 if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00892 continue;
00893 check_post(cdr);
00894 if (ast_tvzero(cdr->end))
00895 cdr->end = ast_tvnow();
00896 if (ast_tvzero(cdr->start)) {
00897 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00898 cdr->disposition = AST_CDR_FAILED;
00899 } else
00900 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00901 if (ast_tvzero(cdr->answer)) {
00902 if (cdr->disposition == AST_CDR_ANSWERED) {
00903 ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
00904 cdr->disposition = AST_CDR_FAILED;
00905 }
00906 } else
00907 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
00908 }
00909 }
00910
00911 char *ast_cdr_disp2str(int disposition)
00912 {
00913 switch (disposition) {
00914 case AST_CDR_NULL:
00915 return "NO ANSWER";
00916 case AST_CDR_NOANSWER:
00917 return "NO ANSWER";
00918 case AST_CDR_FAILED:
00919 return "FAILED";
00920 case AST_CDR_BUSY:
00921 return "BUSY";
00922 case AST_CDR_ANSWERED:
00923 return "ANSWERED";
00924 }
00925 return "UNKNOWN";
00926 }
00927
00928
00929 char *ast_cdr_flags2str(int flag)
00930 {
00931 switch(flag) {
00932 case AST_CDR_OMIT:
00933 return "OMIT";
00934 case AST_CDR_BILLING:
00935 return "BILLING";
00936 case AST_CDR_DOCUMENTATION:
00937 return "DOCUMENTATION";
00938 }
00939 return "Unknown";
00940 }
00941
00942 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00943 {
00944 struct ast_cdr *cdr = chan->cdr;
00945
00946 ast_string_field_set(chan, accountcode, account);
00947 for ( ; cdr ; cdr = cdr->next) {
00948 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00949 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
00950 }
00951 }
00952 return 0;
00953 }
00954
00955 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
00956 {
00957 struct ast_cdr *cdr;
00958 int newflag = ast_cdr_amaflags2int(flag);
00959 if (newflag) {
00960 for (cdr = chan->cdr; cdr; cdr = cdr->next) {
00961 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00962 cdr->amaflags = newflag;
00963 }
00964 }
00965 }
00966
00967 return 0;
00968 }
00969
00970 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
00971 {
00972 struct ast_cdr *cdr = chan->cdr;
00973
00974 for ( ; cdr ; cdr = cdr->next) {
00975 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00976 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
00977 }
00978
00979 return 0;
00980 }
00981
00982 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
00983 {
00984 struct ast_cdr *cdr = chan->cdr;
00985
00986 for ( ; cdr ; cdr = cdr->next) {
00987 int len = strlen(cdr->userfield);
00988
00989 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00990 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
00991 }
00992
00993 return 0;
00994 }
00995
00996 int ast_cdr_update(struct ast_channel *c)
00997 {
00998 struct ast_cdr *cdr = c->cdr;
00999
01000 for ( ; cdr ; cdr = cdr->next) {
01001 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01002 set_one_cid(cdr, c);
01003
01004
01005 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
01006
01007
01008 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
01009 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
01010 }
01011 }
01012
01013 return 0;
01014 }
01015
01016 int ast_cdr_amaflags2int(const char *flag)
01017 {
01018 if (!strcasecmp(flag, "default"))
01019 return 0;
01020 if (!strcasecmp(flag, "omit"))
01021 return AST_CDR_OMIT;
01022 if (!strcasecmp(flag, "billing"))
01023 return AST_CDR_BILLING;
01024 if (!strcasecmp(flag, "documentation"))
01025 return AST_CDR_DOCUMENTATION;
01026 return -1;
01027 }
01028
01029 static void post_cdr(struct ast_cdr *cdr)
01030 {
01031 char *chan;
01032 struct ast_cdr_beitem *i;
01033
01034 for ( ; cdr ; cdr = cdr->next) {
01035 if (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
01036
01037 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01038 continue;
01039 }
01040
01041
01042
01043
01044 if (ast_test_flag(cdr, AST_CDR_FLAG_DIALED) && !ast_test_flag(cdr, AST_CDR_FLAG_ORIGINATED)) {
01045 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01046 continue;
01047 }
01048
01049 chan = S_OR(cdr->channel, "<unknown>");
01050 check_post(cdr);
01051 if (option_verbose > 1 && ast_tvzero(cdr->end))
01052 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks end\n", chan);
01053 if (option_verbose > 1 && ast_tvzero(cdr->start))
01054 ast_verbose(VERBOSE_PREFIX_2 "CDR on channel '%s' lacks start\n", chan);
01055 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01056 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01057 continue;
01058 AST_LIST_LOCK(&be_list);
01059 AST_LIST_TRAVERSE(&be_list, i, list) {
01060 i->be(cdr);
01061 }
01062 AST_LIST_UNLOCK(&be_list);
01063 }
01064 }
01065
01066 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01067 {
01068 struct ast_cdr *dup;
01069 struct ast_flags flags = { 0 };
01070
01071 if (_flags)
01072 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01073
01074 for ( ; cdr ; cdr = cdr->next) {
01075
01076 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01077 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01078 ast_cdr_end(cdr);
01079 if ((dup = ast_cdr_dup(cdr))) {
01080 ast_cdr_detach(dup);
01081 }
01082 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01083 }
01084
01085
01086 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01087 ast_cdr_free_vars(cdr, 0);
01088 }
01089
01090
01091 ast_clear_flag(cdr, AST_FLAGS_ALL);
01092 memset(&cdr->start, 0, sizeof(cdr->start));
01093 memset(&cdr->end, 0, sizeof(cdr->end));
01094 memset(&cdr->answer, 0, sizeof(cdr->answer));
01095 cdr->billsec = 0;
01096 cdr->duration = 0;
01097 ast_cdr_start(cdr);
01098 cdr->disposition = AST_CDR_NOANSWER;
01099 }
01100 }
01101 }
01102
01103 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01104 {
01105 struct ast_flags flags = { 0 };
01106
01107 if (_flags)
01108 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01109
01110
01111 if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) {
01112 ast_clear_flag(cdr, AST_FLAGS_ALL);
01113 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01114 } else {
01115 ast_clear_flag(cdr, AST_FLAGS_ALL);
01116 }
01117
01118 memset(&cdr->start, 0, sizeof(cdr->start));
01119 memset(&cdr->end, 0, sizeof(cdr->end));
01120 memset(&cdr->answer, 0, sizeof(cdr->answer));
01121 cdr->billsec = 0;
01122 cdr->duration = 0;
01123 ast_cdr_start(cdr);
01124 cdr->disposition = AST_CDR_NULL;
01125 }
01126
01127 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01128 {
01129 struct ast_cdr *ret;
01130
01131 if (cdr) {
01132 ret = cdr;
01133
01134 while (cdr->next)
01135 cdr = cdr->next;
01136 cdr->next = newcdr;
01137 } else {
01138 ret = newcdr;
01139 }
01140
01141 return ret;
01142 }
01143
01144
01145 static void reset_batch(void)
01146 {
01147 batch->size = 0;
01148 batch->head = NULL;
01149 batch->tail = NULL;
01150 }
01151
01152
01153 static int init_batch(void)
01154 {
01155
01156 if (!(batch = ast_malloc(sizeof(*batch))))
01157 return -1;
01158
01159 reset_batch();
01160
01161 return 0;
01162 }
01163
01164 static void *do_batch_backend_process(void *data)
01165 {
01166 struct ast_cdr_batch_item *processeditem;
01167 struct ast_cdr_batch_item *batchitem = data;
01168
01169
01170 while (batchitem) {
01171 post_cdr(batchitem->cdr);
01172 ast_cdr_free(batchitem->cdr);
01173 processeditem = batchitem;
01174 batchitem = batchitem->next;
01175 free(processeditem);
01176 }
01177
01178 return NULL;
01179 }
01180
01181 void ast_cdr_submit_batch(int shutdown)
01182 {
01183 struct ast_cdr_batch_item *oldbatchitems = NULL;
01184 pthread_attr_t attr;
01185 pthread_t batch_post_thread = AST_PTHREADT_NULL;
01186
01187
01188 if (!batch || !batch->head)
01189 return;
01190
01191
01192 ast_mutex_lock(&cdr_batch_lock);
01193 oldbatchitems = batch->head;
01194 reset_batch();
01195 ast_mutex_unlock(&cdr_batch_lock);
01196
01197
01198
01199 if (batchscheduleronly || shutdown) {
01200 if (option_debug)
01201 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
01202 do_batch_backend_process(oldbatchitems);
01203 } else {
01204 pthread_attr_init(&attr);
01205 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01206 if (ast_pthread_create_background(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
01207 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01208 do_batch_backend_process(oldbatchitems);
01209 } else {
01210 if (option_debug)
01211 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
01212 }
01213 pthread_attr_destroy(&attr);
01214 }
01215 }
01216
01217 static int submit_scheduled_batch(const void *data)
01218 {
01219 ast_cdr_submit_batch(0);
01220
01221 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01222
01223 return 0;
01224 }
01225
01226 static void submit_unscheduled_batch(void)
01227 {
01228
01229 AST_SCHED_DEL(sched, cdr_sched);
01230
01231 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01232
01233 ast_mutex_lock(&cdr_pending_lock);
01234 ast_cond_signal(&cdr_pending_cond);
01235 ast_mutex_unlock(&cdr_pending_lock);
01236 }
01237
01238 void ast_cdr_detach(struct ast_cdr *cdr)
01239 {
01240 struct ast_cdr_batch_item *newtail;
01241 int curr;
01242
01243 if (!cdr)
01244 return;
01245
01246
01247 if (!enabled) {
01248 if (option_debug)
01249 ast_log(LOG_DEBUG, "Dropping CDR !\n");
01250 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01251 ast_cdr_free(cdr);
01252 return;
01253 }
01254
01255
01256 if (!batchmode) {
01257 post_cdr(cdr);
01258 ast_cdr_free(cdr);
01259 return;
01260 }
01261
01262
01263 if (option_debug)
01264 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
01265
01266
01267 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01268 post_cdr(cdr);
01269 ast_cdr_free(cdr);
01270 return;
01271 }
01272
01273
01274 ast_mutex_lock(&cdr_batch_lock);
01275 if (!batch)
01276 init_batch();
01277 if (!batch->head) {
01278
01279 batch->head = newtail;
01280 } else {
01281
01282 batch->tail->next = newtail;
01283 }
01284 newtail->cdr = cdr;
01285 batch->tail = newtail;
01286 curr = batch->size++;
01287 ast_mutex_unlock(&cdr_batch_lock);
01288
01289
01290 if (curr >= (batchsize - 1))
01291 submit_unscheduled_batch();
01292 }
01293
01294 static void *do_cdr(void *data)
01295 {
01296 struct timespec timeout;
01297 int schedms;
01298 int numevents = 0;
01299
01300 for(;;) {
01301 struct timeval now;
01302 schedms = ast_sched_wait(sched);
01303
01304 if (schedms <= 0)
01305 schedms = 1000;
01306 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01307 timeout.tv_sec = now.tv_sec;
01308 timeout.tv_nsec = now.tv_usec * 1000;
01309
01310 ast_mutex_lock(&cdr_pending_lock);
01311 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01312 numevents = ast_sched_runq(sched);
01313 ast_mutex_unlock(&cdr_pending_lock);
01314 if (option_debug > 1)
01315 ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01316 }
01317
01318 return NULL;
01319 }
01320
01321 static int handle_cli_status(int fd, int argc, char *argv[])
01322 {
01323 struct ast_cdr_beitem *beitem=NULL;
01324 int cnt=0;
01325 long nextbatchtime=0;
01326
01327 if (argc > 2)
01328 return RESULT_SHOWUSAGE;
01329
01330 ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
01331 ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
01332 if (enabled) {
01333 ast_cli(fd, "CDR output unanswered calls: %s\n", unanswered ? "yes" : "no");
01334 if (batchmode) {
01335 if (batch)
01336 cnt = batch->size;
01337 if (cdr_sched > -1)
01338 nextbatchtime = ast_sched_when(sched, cdr_sched);
01339 ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
01340 ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
01341 ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
01342 ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
01343 ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
01344 ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
01345 }
01346 AST_LIST_LOCK(&be_list);
01347 AST_LIST_TRAVERSE(&be_list, beitem, list) {
01348 ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
01349 }
01350 AST_LIST_UNLOCK(&be_list);
01351 }
01352
01353 return 0;
01354 }
01355
01356 static int handle_cli_submit(int fd, int argc, char *argv[])
01357 {
01358 if (argc > 2)
01359 return RESULT_SHOWUSAGE;
01360
01361 submit_unscheduled_batch();
01362 ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01363
01364 return 0;
01365 }
01366
01367 static struct ast_cli_entry cli_submit = {
01368 { "cdr", "submit", NULL },
01369 handle_cli_submit, "Posts all pending batched CDR data",
01370 "Usage: cdr submit\n"
01371 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
01372 };
01373
01374 static struct ast_cli_entry cli_status = {
01375 { "cdr", "status", NULL },
01376 handle_cli_status, "Display the CDR status",
01377 "Usage: cdr status\n"
01378 " Displays the Call Detail Record engine system status.\n"
01379 };
01380
01381 static int do_reload(void)
01382 {
01383 struct ast_config *config;
01384 const char *enabled_value;
01385 const char *unanswered_value;
01386 const char *batched_value;
01387 const char *scheduleronly_value;
01388 const char *batchsafeshutdown_value;
01389 const char *size_value;
01390 const char *time_value;
01391 const char *end_before_h_value;
01392 int cfg_size;
01393 int cfg_time;
01394 int was_enabled;
01395 int was_batchmode;
01396 int res=0;
01397
01398 ast_mutex_lock(&cdr_batch_lock);
01399
01400 batchsize = BATCH_SIZE_DEFAULT;
01401 batchtime = BATCH_TIME_DEFAULT;
01402 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01403 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01404 was_enabled = enabled;
01405 was_batchmode = batchmode;
01406 enabled = 1;
01407 batchmode = 0;
01408
01409
01410 AST_SCHED_DEL(sched, cdr_sched);
01411
01412 if ((config = ast_config_load("cdr.conf"))) {
01413 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01414 enabled = ast_true(enabled_value);
01415 }
01416 if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
01417 unanswered = ast_true(unanswered_value);
01418 }
01419 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01420 batchmode = ast_true(batched_value);
01421 }
01422 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01423 batchscheduleronly = ast_true(scheduleronly_value);
01424 }
01425 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01426 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01427 }
01428 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01429 if (sscanf(size_value, "%30d", &cfg_size) < 1)
01430 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01431 else if (cfg_size < 0)
01432 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01433 else
01434 batchsize = cfg_size;
01435 }
01436 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01437 if (sscanf(time_value, "%30d", &cfg_time) < 1)
01438 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01439 else if (cfg_time < 0)
01440 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01441 else
01442 batchtime = cfg_time;
01443 }
01444 if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01445 ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01446 }
01447
01448 if (enabled && !batchmode) {
01449 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01450 } else if (enabled && batchmode) {
01451 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01452 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01453 } else {
01454 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01455 }
01456
01457
01458
01459 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01460 ast_cond_init(&cdr_pending_cond, NULL);
01461 if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01462 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01463 AST_SCHED_DEL(sched, cdr_sched);
01464 } else {
01465 ast_cli_register(&cli_submit);
01466 ast_register_atexit(ast_cdr_engine_term);
01467 res = 0;
01468 }
01469
01470
01471 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01472
01473 pthread_cancel(cdr_thread);
01474 pthread_kill(cdr_thread, SIGURG);
01475 pthread_join(cdr_thread, NULL);
01476 cdr_thread = AST_PTHREADT_NULL;
01477 ast_cond_destroy(&cdr_pending_cond);
01478 ast_cli_unregister(&cli_submit);
01479 ast_unregister_atexit(ast_cdr_engine_term);
01480 res = 0;
01481
01482
01483 if (!batchmode && was_batchmode) {
01484 ast_cdr_engine_term();
01485 }
01486 } else {
01487 res = 0;
01488 }
01489
01490 ast_mutex_unlock(&cdr_batch_lock);
01491 ast_config_destroy(config);
01492
01493 return res;
01494 }
01495
01496 int ast_cdr_engine_init(void)
01497 {
01498 int res;
01499
01500 sched = sched_context_create();
01501 if (!sched) {
01502 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01503 return -1;
01504 }
01505
01506 ast_cli_register(&cli_status);
01507
01508 res = do_reload();
01509 if (res) {
01510 ast_mutex_lock(&cdr_batch_lock);
01511 res = init_batch();
01512 ast_mutex_unlock(&cdr_batch_lock);
01513 }
01514
01515 return res;
01516 }
01517
01518
01519
01520 void ast_cdr_engine_term(void)
01521 {
01522 ast_cdr_submit_batch(batchsafeshutdown);
01523 }
01524
01525 int ast_cdr_engine_reload(void)
01526 {
01527 return do_reload();
01528 }
01529