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