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