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