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: 11503 $")
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 cdr->disposition = AST_CDR_FAILED;
00525 cdr = cdr->next;
00526 }
00527 }
00528
00529 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00530 {
00531 int res = 0;
00532
00533 while (cdr) {
00534 switch(cause) {
00535 case AST_CAUSE_BUSY:
00536 ast_cdr_busy(cdr);
00537 break;
00538 case AST_CAUSE_FAILURE:
00539 ast_cdr_failed(cdr);
00540 break;
00541 case AST_CAUSE_NORMAL:
00542 break;
00543 case AST_CAUSE_NOTDEFINED:
00544 res = -1;
00545 break;
00546 default:
00547 res = -1;
00548 ast_log(LOG_WARNING, "Cause not handled\n");
00549 }
00550 cdr = cdr->next;
00551 }
00552 return res;
00553 }
00554
00555 void ast_cdr_setdestchan(struct ast_cdr *cdr, char *chann)
00556 {
00557 char *chan;
00558
00559 while (cdr) {
00560 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00561 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00562 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00563 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00564 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00565 cdr = cdr->next;
00566 }
00567 }
00568
00569 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
00570 {
00571 char *chan;
00572
00573 while (cdr) {
00574 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00575 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00576 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00577 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00578 if (!app)
00579 app = "";
00580 ast_copy_string(cdr->lastapp, app, sizeof(cdr->lastapp));
00581 if (!data)
00582 data = "";
00583 ast_copy_string(cdr->lastdata, data, sizeof(cdr->lastdata));
00584 }
00585 cdr = cdr->next;
00586 }
00587 }
00588
00589 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00590 {
00591 char tmp[AST_MAX_EXTENSION] = "";
00592 char *num;
00593
00594 while (cdr) {
00595 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00596
00597 num = c->cid.cid_ani ? c->cid.cid_ani : c->cid.cid_num;
00598
00599 if (c->cid.cid_name && num)
00600 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
00601 else if (c->cid.cid_name)
00602 ast_copy_string(tmp, c->cid.cid_name, sizeof(tmp));
00603 else if (num)
00604 ast_copy_string(tmp, num, sizeof(tmp));
00605 ast_copy_string(cdr->clid, tmp, sizeof(cdr->clid));
00606 ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
00607 }
00608 cdr = cdr->next;
00609 }
00610
00611 return 0;
00612 }
00613
00614
00615 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00616 {
00617 char *chan;
00618 char *num;
00619 char tmp[AST_MAX_EXTENSION] = "";
00620
00621 while (cdr) {
00622 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00623 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00624 if (!ast_strlen_zero(cdr->channel))
00625 ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan);
00626 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00627
00628 num = c->cid.cid_ani ? c->cid.cid_ani : c->cid.cid_num;
00629
00630 if (c->cid.cid_name && num)
00631 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
00632 else if (c->cid.cid_name)
00633 ast_copy_string(tmp, c->cid.cid_name, sizeof(tmp));
00634 else if (num)
00635 ast_copy_string(tmp, num, sizeof(tmp));
00636 ast_copy_string(cdr->clid, tmp, sizeof(cdr->clid));
00637 ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
00638
00639 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER;
00640 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
00641 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00642
00643 ast_copy_string(cdr->dst, c->exten, sizeof(cdr->dst));
00644 ast_copy_string(cdr->dcontext, c->context, sizeof(cdr->dcontext));
00645
00646 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00647 }
00648 cdr = cdr->next;
00649 }
00650 return 0;
00651 }
00652
00653 void ast_cdr_end(struct ast_cdr *cdr)
00654 {
00655 char *chan;
00656
00657 while (cdr) {
00658 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00659 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00660 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00661 if (ast_tvzero(cdr->start))
00662 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", chan);
00663 if (ast_tvzero(cdr->end))
00664 cdr->end = ast_tvnow();
00665 cdr = cdr->next;
00666 }
00667 }
00668
00669 char *ast_cdr_disp2str(int disposition)
00670 {
00671 switch (disposition) {
00672 case AST_CDR_NOANSWER:
00673 return "NO ANSWER";
00674 case AST_CDR_FAILED:
00675 return "FAILED";
00676 case AST_CDR_BUSY:
00677 return "BUSY";
00678 case AST_CDR_ANSWERED:
00679 return "ANSWERED";
00680 }
00681 return "UNKNOWN";
00682 }
00683
00684
00685 char *ast_cdr_flags2str(int flag)
00686 {
00687 switch(flag) {
00688 case AST_CDR_OMIT:
00689 return "OMIT";
00690 case AST_CDR_BILLING:
00691 return "BILLING";
00692 case AST_CDR_DOCUMENTATION:
00693 return "DOCUMENTATION";
00694 }
00695 return "Unknown";
00696 }
00697
00698 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00699 {
00700 struct ast_cdr *cdr = chan->cdr;
00701
00702 ast_copy_string(chan->accountcode, account, sizeof(chan->accountcode));
00703 while (cdr) {
00704 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00705 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
00706 cdr = cdr->next;
00707 }
00708 return 0;
00709 }
00710
00711 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
00712 {
00713 struct ast_cdr *cdr;
00714 int newflag;
00715
00716 newflag = ast_cdr_amaflags2int(flag);
00717 if (newflag) {
00718 for (cdr = chan->cdr; cdr; cdr = cdr->next) {
00719 cdr->amaflags = newflag;
00720 }
00721 }
00722
00723 return 0;
00724 }
00725
00726 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
00727 {
00728 struct ast_cdr *cdr = chan->cdr;
00729
00730 while (cdr) {
00731 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00732 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
00733 cdr = cdr->next;
00734 }
00735
00736 return 0;
00737 }
00738
00739 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
00740 {
00741 struct ast_cdr *cdr = chan->cdr;
00742
00743 while (cdr) {
00744 int len = strlen(cdr->userfield);
00745
00746 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00747 strncpy(cdr->userfield+len, userfield, sizeof(cdr->userfield) - len - 1);
00748
00749 cdr = cdr->next;
00750 }
00751
00752 return 0;
00753 }
00754
00755 int ast_cdr_update(struct ast_channel *c)
00756 {
00757 struct ast_cdr *cdr = c->cdr;
00758 char *num;
00759 char tmp[AST_MAX_EXTENSION] = "";
00760
00761 while (cdr) {
00762 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00763 num = c->cid.cid_ani ? c->cid.cid_ani : c->cid.cid_num;
00764
00765 if (c->cid.cid_name && num)
00766 snprintf(tmp, sizeof(tmp), "\"%s\" <%s>", c->cid.cid_name, num);
00767 else if (c->cid.cid_name)
00768 ast_copy_string(tmp, c->cid.cid_name, sizeof(tmp));
00769 else if (num)
00770 ast_copy_string(tmp, num, sizeof(tmp));
00771 ast_copy_string(cdr->clid, tmp, sizeof(cdr->clid));
00772 ast_copy_string(cdr->src, num ? num : "", sizeof(cdr->src));
00773
00774
00775 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00776
00777 ast_copy_string(cdr->dst, (ast_strlen_zero(c->macroexten)) ? c->exten : c->macroexten, sizeof(cdr->dst));
00778 ast_copy_string(cdr->dcontext, (ast_strlen_zero(c->macrocontext)) ? c->context : c->macrocontext, sizeof(cdr->dcontext));
00779 }
00780 cdr = cdr->next;
00781 }
00782
00783 return 0;
00784 }
00785
00786 int ast_cdr_amaflags2int(const char *flag)
00787 {
00788 if (!strcasecmp(flag, "default"))
00789 return 0;
00790 if (!strcasecmp(flag, "omit"))
00791 return AST_CDR_OMIT;
00792 if (!strcasecmp(flag, "billing"))
00793 return AST_CDR_BILLING;
00794 if (!strcasecmp(flag, "documentation"))
00795 return AST_CDR_DOCUMENTATION;
00796 return -1;
00797 }
00798
00799 static void post_cdr(struct ast_cdr *cdr)
00800 {
00801 char *chan;
00802 struct ast_cdr_beitem *i;
00803
00804 while (cdr) {
00805 chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
00806 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00807 ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
00808 if (ast_tvzero(cdr->end))
00809 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
00810 if (ast_tvzero(cdr->start))
00811 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
00812 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec + (cdr->end.tv_usec - cdr->start.tv_usec) / 1000000;
00813 if (!ast_tvzero(cdr->answer))
00814 cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec + (cdr->end.tv_usec - cdr->answer.tv_usec) / 1000000;
00815 else
00816 cdr->billsec = 0;
00817 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
00818 AST_LIST_LOCK(&be_list);
00819 AST_LIST_TRAVERSE(&be_list, i, list) {
00820 i->be(cdr);
00821 }
00822 AST_LIST_UNLOCK(&be_list);
00823 cdr = cdr->next;
00824 }
00825 }
00826
00827 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
00828 {
00829 struct ast_cdr *dup;
00830 struct ast_flags flags = { 0 };
00831
00832 if (_flags)
00833 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
00834
00835 while (cdr) {
00836
00837 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00838 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
00839 ast_cdr_end(cdr);
00840 if ((dup = ast_cdr_dup(cdr))) {
00841 ast_cdr_detach(dup);
00842 }
00843 ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
00844 }
00845
00846
00847 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
00848 ast_cdr_free_vars(cdr, 0);
00849 }
00850
00851
00852 ast_clear_flag(cdr, AST_FLAGS_ALL);
00853 memset(&cdr->start, 0, sizeof(cdr->start));
00854 memset(&cdr->end, 0, sizeof(cdr->end));
00855 memset(&cdr->answer, 0, sizeof(cdr->answer));
00856 cdr->billsec = 0;
00857 cdr->duration = 0;
00858 ast_cdr_start(cdr);
00859 cdr->disposition = AST_CDR_NOANSWER;
00860 }
00861
00862 cdr = cdr->next;
00863 }
00864 }
00865
00866 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
00867 {
00868 struct ast_cdr *ret;
00869
00870 if (cdr) {
00871 ret = cdr;
00872
00873 while (cdr->next)
00874 cdr = cdr->next;
00875 cdr->next = newcdr;
00876 } else {
00877 ret = newcdr;
00878 }
00879
00880 return ret;
00881 }
00882
00883
00884 static void reset_batch(void)
00885 {
00886 batch->size = 0;
00887 batch->head = NULL;
00888 batch->tail = NULL;
00889 }
00890
00891
00892 static int init_batch(void)
00893 {
00894
00895 batch = malloc(sizeof(*batch));
00896 if (!batch) {
00897 ast_log(LOG_WARNING, "CDR: out of memory while trying to handle batched records, data will most likely be lost\n");
00898 return -1;
00899 }
00900
00901 reset_batch();
00902
00903 return 0;
00904 }
00905
00906 static void *do_batch_backend_process(void *data)
00907 {
00908 struct ast_cdr_batch_item *processeditem;
00909 struct ast_cdr_batch_item *batchitem = data;
00910
00911
00912 while (batchitem) {
00913 post_cdr(batchitem->cdr);
00914 ast_cdr_free(batchitem->cdr);
00915 processeditem = batchitem;
00916 batchitem = batchitem->next;
00917 free(processeditem);
00918 }
00919
00920 return NULL;
00921 }
00922
00923 void ast_cdr_submit_batch(int shutdown)
00924 {
00925 struct ast_cdr_batch_item *oldbatchitems = NULL;
00926 pthread_attr_t attr;
00927 pthread_t batch_post_thread = AST_PTHREADT_NULL;
00928
00929
00930 if (!batch || !batch->head)
00931 return;
00932
00933
00934 ast_mutex_lock(&cdr_batch_lock);
00935 oldbatchitems = batch->head;
00936 reset_batch();
00937 ast_mutex_unlock(&cdr_batch_lock);
00938
00939
00940
00941 if (batchscheduleronly || shutdown) {
00942 if (option_debug)
00943 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n");
00944 do_batch_backend_process(oldbatchitems);
00945 } else {
00946 pthread_attr_init(&attr);
00947 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00948 if (ast_pthread_create(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) {
00949 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
00950 do_batch_backend_process(oldbatchitems);
00951 } else {
00952 if (option_debug)
00953 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n");
00954 }
00955 }
00956 }
00957
00958 static int submit_scheduled_batch(void *data)
00959 {
00960 ast_cdr_submit_batch(0);
00961
00962 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
00963
00964 return 0;
00965 }
00966
00967 static void submit_unscheduled_batch(void)
00968 {
00969
00970 if (cdr_sched > -1)
00971 ast_sched_del(sched, cdr_sched);
00972
00973 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
00974
00975 ast_mutex_lock(&cdr_pending_lock);
00976 ast_cond_signal(&cdr_pending_cond);
00977 ast_mutex_unlock(&cdr_pending_lock);
00978 }
00979
00980 void ast_cdr_detach(struct ast_cdr *cdr)
00981 {
00982 struct ast_cdr_batch_item *newtail;
00983 int curr;
00984
00985
00986 if (!enabled) {
00987 if (option_debug)
00988 ast_log(LOG_DEBUG, "Dropping CDR !\n");
00989 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
00990 ast_cdr_free(cdr);
00991 return;
00992 }
00993
00994
00995 if (!batchmode) {
00996 post_cdr(cdr);
00997 ast_cdr_free(cdr);
00998 return;
00999 }
01000
01001
01002 if (option_debug)
01003 ast_log(LOG_DEBUG, "CDR detaching from this thread\n");
01004
01005
01006 newtail = malloc(sizeof(*newtail));
01007 if (!newtail) {
01008 ast_log(LOG_WARNING, "CDR: out of memory while trying to detach, will try in this thread instead\n");
01009 post_cdr(cdr);
01010 ast_cdr_free(cdr);
01011 return;
01012 }
01013 memset(newtail, 0, sizeof(*newtail));
01014
01015
01016 ast_mutex_lock(&cdr_batch_lock);
01017 if (!batch)
01018 init_batch();
01019 if (!batch->head) {
01020
01021 batch->head = newtail;
01022 } else {
01023
01024 batch->tail->next = newtail;
01025 }
01026 newtail->cdr = cdr;
01027 batch->tail = newtail;
01028 curr = batch->size++;
01029 ast_mutex_unlock(&cdr_batch_lock);
01030
01031
01032 if (curr >= (batchsize - 1))
01033 submit_unscheduled_batch();
01034 }
01035
01036 static void *do_cdr(void *data)
01037 {
01038 struct timespec timeout;
01039 int schedms;
01040 int numevents = 0;
01041
01042 for(;;) {
01043 struct timeval now = ast_tvnow();
01044 schedms = ast_sched_wait(sched);
01045
01046 if (schedms <= 0)
01047 schedms = 1000;
01048 timeout.tv_sec = now.tv_sec + (schedms / 1000);
01049 timeout.tv_nsec = (now.tv_usec * 1000) + ((schedms % 1000) * 1000);
01050
01051 ast_mutex_lock(&cdr_pending_lock);
01052 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01053 numevents = ast_sched_runq(sched);
01054 ast_mutex_unlock(&cdr_pending_lock);
01055 if (option_debug > 1)
01056 ast_log(LOG_DEBUG, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01057 }
01058
01059 return NULL;
01060 }
01061
01062 static int handle_cli_status(int fd, int argc, char *argv[])
01063 {
01064 struct ast_cdr_beitem *beitem=NULL;
01065 int cnt=0;
01066 long nextbatchtime=0;
01067
01068 if (argc > 2)
01069 return RESULT_SHOWUSAGE;
01070
01071 ast_cli(fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
01072 ast_cli(fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
01073 if (enabled) {
01074 if (batchmode) {
01075 if (batch)
01076 cnt = batch->size;
01077 if (cdr_sched > -1)
01078 nextbatchtime = ast_sched_when(sched, cdr_sched);
01079 ast_cli(fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
01080 ast_cli(fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
01081 ast_cli(fd, "CDR current batch size: %d record%s\n", cnt, (cnt != 1) ? "s" : "");
01082 ast_cli(fd, "CDR maximum batch size: %d record%s\n", batchsize, (batchsize != 1) ? "s" : "");
01083 ast_cli(fd, "CDR maximum batch time: %d second%s\n", batchtime, (batchtime != 1) ? "s" : "");
01084 ast_cli(fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, (nextbatchtime != 1) ? "s" : "");
01085 }
01086 AST_LIST_LOCK(&be_list);
01087 AST_LIST_TRAVERSE(&be_list, beitem, list) {
01088 ast_cli(fd, "CDR registered backend: %s\n", beitem->name);
01089 }
01090 AST_LIST_UNLOCK(&be_list);
01091 }
01092
01093 return 0;
01094 }
01095
01096 static int handle_cli_submit(int fd, int argc, char *argv[])
01097 {
01098 if (argc > 2)
01099 return RESULT_SHOWUSAGE;
01100
01101 submit_unscheduled_batch();
01102 ast_cli(fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
01103
01104 return 0;
01105 }
01106
01107 static struct ast_cli_entry cli_submit = {
01108 .cmda = { "cdr", "submit", NULL },
01109 .handler = handle_cli_submit,
01110 .summary = "Posts all pending batched CDR data",
01111 .usage =
01112 "Usage: cdr submit\n"
01113 " Posts all pending batched CDR data to the configured CDR backend engine modules.\n"
01114 };
01115
01116 static struct ast_cli_entry cli_status = {
01117 .cmda = { "cdr", "status", NULL },
01118 .handler = handle_cli_status,
01119 .summary = "Display the CDR status",
01120 .usage =
01121 "Usage: cdr status\n"
01122 " Displays the Call Detail Record engine system status.\n"
01123 };
01124
01125 static int do_reload(void)
01126 {
01127 struct ast_config *config;
01128 const char *enabled_value;
01129 const char *batched_value;
01130 const char *scheduleronly_value;
01131 const char *batchsafeshutdown_value;
01132 const char *size_value;
01133 const char *time_value;
01134 int cfg_size;
01135 int cfg_time;
01136 int was_enabled;
01137 int was_batchmode;
01138 int res=0;
01139
01140 ast_mutex_lock(&cdr_batch_lock);
01141
01142 batchsize = BATCH_SIZE_DEFAULT;
01143 batchtime = BATCH_TIME_DEFAULT;
01144 batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01145 batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01146 was_enabled = enabled;
01147 was_batchmode = batchmode;
01148 enabled = 1;
01149 batchmode = 0;
01150
01151
01152 if (cdr_sched > -1)
01153 ast_sched_del(sched, cdr_sched);
01154
01155 if ((config = ast_config_load("cdr.conf"))) {
01156 if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01157 enabled = ast_true(enabled_value);
01158 }
01159 if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01160 batchmode = ast_true(batched_value);
01161 }
01162 if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01163 batchscheduleronly = ast_true(scheduleronly_value);
01164 }
01165 if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01166 batchsafeshutdown = ast_true(batchsafeshutdown_value);
01167 }
01168 if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01169 if (sscanf(size_value, "%d", &cfg_size) < 1)
01170 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01171 else if (size_value < 0)
01172 ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01173 else
01174 batchsize = cfg_size;
01175 }
01176 if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01177 if (sscanf(time_value, "%d", &cfg_time) < 1)
01178 ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01179 else if (time_value < 0)
01180 ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01181 else
01182 batchtime = cfg_time;
01183 }
01184 }
01185
01186 if (enabled && !batchmode) {
01187 ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01188 } else if (enabled && batchmode) {
01189 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01190 ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01191 } else {
01192 ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01193 }
01194
01195
01196
01197 if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01198 ast_cond_init(&cdr_pending_cond, NULL);
01199 if (ast_pthread_create(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01200 ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01201 ast_sched_del(sched, cdr_sched);
01202 } else {
01203 ast_cli_register(&cli_submit);
01204 ast_register_atexit(ast_cdr_engine_term);
01205 res = 0;
01206 }
01207
01208
01209 } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01210
01211 pthread_cancel(cdr_thread);
01212 pthread_kill(cdr_thread, SIGURG);
01213 pthread_join(cdr_thread, NULL);
01214 cdr_thread = AST_PTHREADT_NULL;
01215 ast_cond_destroy(&cdr_pending_cond);
01216 ast_cli_unregister(&cli_submit);
01217 ast_unregister_atexit(ast_cdr_engine_term);
01218 res = 0;
01219
01220
01221 if (!batchmode && was_batchmode) {
01222 ast_cdr_engine_term();
01223 }
01224 } else {
01225 res = 0;
01226 }
01227
01228 ast_mutex_unlock(&cdr_batch_lock);
01229 ast_config_destroy(config);
01230
01231 return res;
01232 }
01233
01234 int ast_cdr_engine_init(void)
01235 {
01236 int res;
01237
01238 sched = sched_context_create();
01239 if (!sched) {
01240 ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01241 return -1;
01242 }
01243
01244 ast_cli_register(&cli_status);
01245
01246 res = do_reload();
01247 if (res) {
01248 ast_mutex_lock(&cdr_batch_lock);
01249 res = init_batch();
01250 ast_mutex_unlock(&cdr_batch_lock);
01251 }
01252
01253 return res;
01254 }
01255
01256
01257
01258 void ast_cdr_engine_term(void)
01259 {
01260 ast_cdr_submit_batch(batchsafeshutdown);
01261 }
01262
01263 void ast_cdr_engine_reload(void)
01264 {
01265 do_reload();
01266 }
01267