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