![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
cdr.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Call Detail Record API 00022 * 00023 * \author Mark Spencer <markster@digium.com> 00024 * 00025 * \note Includes code and algorithms from the Zapata library. 00026 * 00027 * \note We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip 00028 * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally 00029 * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR 00030 * isn't properly generated and posted. 00031 */ 00032 00033 00034 #include "asterisk.h" 00035 00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 53128 $") 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 /*! Default AMA flag for billing records (CDR's) */ 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; 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 batchmode; 00093 static int batchsize; 00094 static int batchtime; 00095 static int batchscheduleronly; 00096 static int batchsafeshutdown; 00097 00098 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock); 00099 00100 /* these are used to wake up the CDR thread when there's work to do */ 00101 AST_MUTEX_DEFINE_STATIC(cdr_pending_lock); 00102 static ast_cond_t cdr_pending_cond; 00103 00104 00105 /*! Register a CDR driver. Each registered CDR driver generates a CDR 00106 \return 0 on success, -1 on failure 00107 */ 00108 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be) 00109 { 00110 struct ast_cdr_beitem *i; 00111 00112 if (!name) 00113 return -1; 00114 if (!be) { 00115 ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name); 00116 return -1; 00117 } 00118 00119 AST_LIST_LOCK(&be_list); 00120 AST_LIST_TRAVERSE(&be_list, i, list) { 00121 if (!strcasecmp(name, i->name)) 00122 break; 00123 } 00124 AST_LIST_UNLOCK(&be_list); 00125 00126 if (i) { 00127 ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name); 00128 return -1; 00129 } 00130 00131 if (!(i = ast_calloc(1, sizeof(*i)))) 00132 return -1; 00133 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 /*! unregister a CDR driver */ 00146 void ast_cdr_unregister(const 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 /*! Duplicate a CDR record 00165 \returns Pointer to new CDR record 00166 */ 00167 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr) 00168 { 00169 struct ast_cdr *newcdr = ast_cdr_alloc(); 00170 00171 if (!newcdr) 00172 return NULL; 00173 00174 memcpy(newcdr, cdr, sizeof(*newcdr)); 00175 /* The varshead is unusable, volatile even, after the memcpy so we take care of that here */ 00176 memset(&newcdr->varshead, 0, sizeof(newcdr->varshead)); 00177 ast_cdr_copy_vars(newcdr, cdr); 00178 newcdr->next = NULL; 00179 00180 return newcdr; 00181 } 00182 00183 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur) 00184 { 00185 if (ast_strlen_zero(name)) 00186 return NULL; 00187 00188 for (; cdr; cdr = recur ? cdr->next : NULL) { 00189 struct ast_var_t *variables; 00190 struct varshead *headp = &cdr->varshead; 00191 AST_LIST_TRAVERSE(headp, variables, entries) { 00192 if (!strcasecmp(name, ast_var_name(variables))) 00193 return ast_var_value(variables); 00194 } 00195 } 00196 00197 return NULL; 00198 } 00199 00200 static void cdr_get_tv(struct timeval tv, const char *fmt, char *buf, int bufsize) 00201 { 00202 if (fmt == NULL) { /* raw mode */ 00203 snprintf(buf, bufsize, "%ld.%06ld", (long)tv.tv_sec, (long)tv.tv_usec); 00204 } else { 00205 time_t t = tv.tv_sec; 00206 if (t) { 00207 struct tm tm; 00208 localtime_r(&t, &tm); 00209 strftime(buf, bufsize, fmt, &tm); 00210 } 00211 } 00212 } 00213 00214 /*! CDR channel variable retrieval */ 00215 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw) 00216 { 00217 const char *fmt = "%Y-%m-%d %T"; 00218 const char *varbuf; 00219 00220 *ret = NULL; 00221 /* special vars (the ones from the struct ast_cdr when requested by name) 00222 I'd almost say we should convert all the stringed vals to vars */ 00223 00224 if (!strcasecmp(name, "clid")) 00225 ast_copy_string(workspace, cdr->clid, workspacelen); 00226 else if (!strcasecmp(name, "src")) 00227 ast_copy_string(workspace, cdr->src, workspacelen); 00228 else if (!strcasecmp(name, "dst")) 00229 ast_copy_string(workspace, cdr->dst, workspacelen); 00230 else if (!strcasecmp(name, "dcontext")) 00231 ast_copy_string(workspace, cdr->dcontext, workspacelen); 00232 else if (!strcasecmp(name, "channel")) 00233 ast_copy_string(workspace, cdr->channel, workspacelen); 00234 else if (!strcasecmp(name, "dstchannel")) 00235 ast_copy_string(workspace, cdr->dstchannel, workspacelen); 00236 else if (!strcasecmp(name, "lastapp")) 00237 ast_copy_string(workspace, cdr->lastapp, workspacelen); 00238 else if (!strcasecmp(name, "lastdata")) 00239 ast_copy_string(workspace, cdr->lastdata, workspacelen); 00240 else if (!strcasecmp(name, "start")) 00241 cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen); 00242 else if (!strcasecmp(name, "answer")) 00243 cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen); 00244 else if (!strcasecmp(name, "end")) 00245 cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen); 00246 else if (!strcasecmp(name, "duration")) 00247 snprintf(workspace, workspacelen, "%ld", cdr->duration); 00248 else if (!strcasecmp(name, "billsec")) 00249 snprintf(workspace, workspacelen, "%ld", cdr->billsec); 00250 else if (!strcasecmp(name, "disposition")) { 00251 if (raw) { 00252 snprintf(workspace, workspacelen, "%ld", cdr->disposition); 00253 } else { 00254 ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen); 00255 } 00256 } else if (!strcasecmp(name, "amaflags")) { 00257 if (raw) { 00258 snprintf(workspace, workspacelen, "%ld", cdr->amaflags); 00259 } else { 00260 ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen); 00261 } 00262 } else if (!strcasecmp(name, "accountcode")) 00263 ast_copy_string(workspace, cdr->accountcode, workspacelen); 00264 else if (!strcasecmp(name, "uniqueid")) 00265 ast_copy_string(workspace, cdr->uniqueid, workspacelen); 00266 else if (!strcasecmp(name, "userfield")) 00267 ast_copy_string(workspace, cdr->userfield, workspacelen); 00268 else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur))) 00269 ast_copy_string(workspace, varbuf, workspacelen); 00270 else 00271 workspace[0] = '\0'; 00272 00273 if (!ast_strlen_zero(workspace)) 00274 *ret = workspace; 00275 } 00276 00277 /* readonly cdr variables */ 00278 static const char *cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel", 00279 "lastapp", "lastdata", "start", "answer", "end", "duration", 00280 "billsec", "disposition", "amaflags", "accountcode", "uniqueid", 00281 "userfield", NULL }; 00282 /*! Set a CDR channel variable 00283 \note You can't set the CDR variables that belong to the actual CDR record, like "billsec". 00284 */ 00285 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur) 00286 { 00287 struct ast_var_t *newvariable; 00288 struct varshead *headp; 00289 int x; 00290 00291 for (x = 0; cdr_readonly_vars[x]; x++) { 00292 if (!strcasecmp(name, cdr_readonly_vars[x])) { 00293 ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name); 00294 return -1; 00295 } 00296 } 00297 00298 if (!cdr) { 00299 ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n"); 00300 return -1; 00301 } 00302 00303 for (; cdr; cdr = recur ? cdr->next : NULL) { 00304 headp = &cdr->varshead; 00305 AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) { 00306 if (!strcasecmp(ast_var_name(newvariable), name)) { 00307 /* there is already such a variable, delete it */ 00308 AST_LIST_REMOVE_CURRENT(headp, entries); 00309 ast_var_delete(newvariable); 00310 break; 00311 } 00312 } 00313 AST_LIST_TRAVERSE_SAFE_END; 00314 00315 if (value) { 00316 newvariable = ast_var_assign(name, value); 00317 AST_LIST_INSERT_HEAD(headp, newvariable, entries); 00318 } 00319 } 00320 00321 return 0; 00322 } 00323 00324 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr) 00325 { 00326 struct ast_var_t *variables, *newvariable = NULL; 00327 struct varshead *headpa, *headpb; 00328 const char *var, *val; 00329 int x = 0; 00330 00331 headpa = &from_cdr->varshead; 00332 headpb = &to_cdr->varshead; 00333 00334 AST_LIST_TRAVERSE(headpa,variables,entries) { 00335 if (variables && 00336 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) && 00337 !ast_strlen_zero(var) && !ast_strlen_zero(val)) { 00338 newvariable = ast_var_assign(var, val); 00339 AST_LIST_INSERT_HEAD(headpb, newvariable, entries); 00340 x++; 00341 } 00342 } 00343 00344 return x; 00345 } 00346 00347 int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur) 00348 { 00349 struct ast_var_t *variables; 00350 const char *var, *val; 00351 char *tmp; 00352 char workspace[256]; 00353 int total = 0, x = 0, i; 00354 00355 (*buf)->used = 0; 00356 (*buf)->str[0] = '\0'; 00357 00358 for (; cdr; cdr = recur ? cdr->next : NULL) { 00359 if (++x > 1) 00360 ast_str_append(buf, 0, "\n"); 00361 00362 AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) { 00363 if (variables && 00364 (var = ast_var_name(variables)) && (val = ast_var_value(variables)) && 00365 !ast_strlen_zero(var) && !ast_strlen_zero(val)) { 00366 if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, val, sep) < 0) { 00367 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n"); 00368 break; 00369 } else 00370 total++; 00371 } else 00372 break; 00373 } 00374 00375 for (i = 0; cdr_readonly_vars[i]; i++) { 00376 ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0); 00377 if (!tmp) 00378 continue; 00379 00380 if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) { 00381 ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n"); 00382 break; 00383 } else 00384 total++; 00385 } 00386 } 00387 00388 return total; 00389 } 00390 00391 00392 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur) 00393 { 00394 00395 /* clear variables */ 00396 for (; cdr; cdr = recur ? cdr->next : NULL) { 00397 struct ast_var_t *vardata; 00398 struct varshead *headp = &cdr->varshead; 00399 while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) 00400 ast_var_delete(vardata); 00401 } 00402 } 00403 00404 /*! \brief print a warning if cdr already posted */ 00405 static void check_post(struct ast_cdr *cdr) 00406 { 00407 if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED)) 00408 ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>")); 00409 } 00410 00411 /*! \brief print a warning if cdr already started */ 00412 static void check_start(struct ast_cdr *cdr) 00413 { 00414 if (!ast_tvzero(cdr->start)) 00415 ast_log(LOG_NOTICE, "CDR on channel '%s' already started\n", S_OR(cdr->channel, "<unknown>")); 00416 } 00417 00418 void ast_cdr_free(struct ast_cdr *cdr) 00419 { 00420 00421 while (cdr) { 00422 struct ast_cdr *next = cdr->next; 00423 char *chan = S_OR(cdr->channel, "<unknown>"); 00424 if (!ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) 00425 ast_log(LOG_NOTICE, "CDR on channel '%s' not posted\n", chan); 00426 if (ast_tvzero(cdr->end)) 00427 ast_log(LOG_NOTICE, "CDR on channel '%s' lacks end\n", chan); 00428 if (ast_tvzero(cdr->start)) 00429 ast_log(LOG_NOTICE, "CDR on channel '%s' lacks start\n", chan); 00430 00431 ast_cdr_free_vars(cdr, 0); 00432 free(cdr); 00433 cdr = next; 00434 } 00435 } 00436 00437 struct ast_cdr *ast_cdr_alloc(void) 00438 { 00439 return ast_calloc(1, sizeof(struct ast_cdr)); 00440 } 00441 00442 void ast_cdr_start(struct ast_cdr *cdr) 00443 { 00444 char *chan; 00445 00446 for (; cdr; cdr = cdr->next) { 00447 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00448 chan = S_OR(cdr->channel, "<unknown>"); 00449 check_post(cdr); 00450 check_start(cdr); 00451 cdr->start = ast_tvnow(); 00452 } 00453 } 00454 } 00455 00456 void ast_cdr_answer(struct ast_cdr *cdr) 00457 { 00458 00459 for (; cdr; cdr = cdr->next) { 00460 check_post(cdr); 00461 if (cdr->disposition < AST_CDR_ANSWERED) 00462 cdr->disposition = AST_CDR_ANSWERED; 00463 if (ast_tvzero(cdr->answer)) 00464 cdr->answer = ast_tvnow(); 00465 } 00466 } 00467 00468 void ast_cdr_busy(struct ast_cdr *cdr) 00469 { 00470 00471 for (; cdr; cdr = cdr->next) { 00472 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00473 check_post(cdr); 00474 if (cdr->disposition < AST_CDR_BUSY) 00475 cdr->disposition = AST_CDR_BUSY; 00476 } 00477 } 00478 } 00479 00480 void ast_cdr_failed(struct ast_cdr *cdr) 00481 { 00482 for (; cdr; cdr = cdr->next) { 00483 check_post(cdr); 00484 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00485 if (cdr->disposition < AST_CDR_FAILED) 00486 cdr->disposition = AST_CDR_FAILED; 00487 } 00488 } 00489 } 00490 00491 int ast_cdr_disposition(struct ast_cdr *cdr, int cause) 00492 { 00493 int res = 0; 00494 00495 for (; cdr; cdr = cdr->next) { 00496 switch (cause) { 00497 case AST_CAUSE_BUSY: 00498 ast_cdr_busy(cdr); 00499 break; 00500 case AST_CAUSE_FAILURE: 00501 ast_cdr_failed(cdr); 00502 break; 00503 case AST_CAUSE_NORMAL: 00504 break; 00505 case AST_CAUSE_NOTDEFINED: 00506 res = -1; 00507 break; 00508 default: 00509 res = -1; 00510 ast_log(LOG_WARNING, "Cause not handled\n"); 00511 } 00512 } 00513 return res; 00514 } 00515 00516 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann) 00517 { 00518 for (; cdr; cdr = cdr->next) { 00519 check_post(cdr); 00520 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 00521 ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel)); 00522 } 00523 } 00524 00525 void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data) 00526 { 00527 00528 for (; cdr; cdr = cdr->next) { 00529 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00530 check_post(cdr); 00531 if (!app) 00532 app = ""; 00533 ast_copy_string(cdr->lastapp, app, sizeof(cdr->lastapp)); 00534 if (!data) 00535 data = ""; 00536 ast_copy_string(cdr->lastdata, data, sizeof(cdr->lastdata)); 00537 } 00538 } 00539 } 00540 00541 /* set cid info for one record */ 00542 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c) 00543 { 00544 /* Grab source from ANI or normal Caller*ID */ 00545 const char *num = S_OR(c->cid.cid_ani, c->cid.cid_num); 00546 00547 if (!ast_strlen_zero(c->cid.cid_name)) { 00548 if (!ast_strlen_zero(num)) /* both name and number */ 00549 snprintf(cdr->clid, sizeof(cdr->clid), "\"%s\" <%s>", c->cid.cid_name, num); 00550 else /* only name */ 00551 ast_copy_string(cdr->clid, c->cid.cid_name, sizeof(cdr->clid)); 00552 } else if (!ast_strlen_zero(num)) { /* only number */ 00553 ast_copy_string(cdr->clid, num, sizeof(cdr->clid)); 00554 } else { /* nothing known */ 00555 cdr->clid[0] = '\0'; 00556 } 00557 ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src)); 00558 00559 } 00560 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c) 00561 { 00562 for (; cdr; cdr = cdr->next) { 00563 if (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 00564 set_one_cid(cdr, c); 00565 } 00566 return 0; 00567 } 00568 00569 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c) 00570 { 00571 char *chan; 00572 00573 for ( ; cdr ; cdr = cdr->next) { 00574 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00575 chan = S_OR(cdr->channel, "<unknown>"); 00576 if (!ast_strlen_zero(cdr->channel)) 00577 ast_log(LOG_WARNING, "CDR already initialized on '%s'\n", chan); 00578 ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel)); 00579 set_one_cid(cdr, c); 00580 00581 cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NOANSWER; 00582 cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags; 00583 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode)); 00584 /* Destination information */ 00585 ast_copy_string(cdr->dst, c->exten, sizeof(cdr->dst)); 00586 ast_copy_string(cdr->dcontext, c->context, sizeof(cdr->dcontext)); 00587 /* Unique call identifier */ 00588 ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid)); 00589 } 00590 } 00591 return 0; 00592 } 00593 00594 void ast_cdr_end(struct ast_cdr *cdr) 00595 { 00596 for ( ; cdr ; cdr = cdr->next) { 00597 check_post(cdr); 00598 if (ast_tvzero(cdr->end)) 00599 cdr->end = ast_tvnow(); 00600 if (ast_tvzero(cdr->start)) { 00601 ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>")); 00602 cdr->disposition = AST_CDR_FAILED; 00603 } else 00604 cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec; 00605 cdr->billsec = ast_tvzero(cdr->answer) ? 0 : cdr->end.tv_sec - cdr->answer.tv_sec; 00606 } 00607 } 00608 00609 char *ast_cdr_disp2str(int disposition) 00610 { 00611 switch (disposition) { 00612 case AST_CDR_NOANSWER: 00613 return "NO ANSWER"; 00614 case AST_CDR_FAILED: 00615 return "FAILED"; 00616 case AST_CDR_BUSY: 00617 return "BUSY"; 00618 case AST_CDR_ANSWERED: 00619 return "ANSWERED"; 00620 } 00621 return "UNKNOWN"; 00622 } 00623 00624 /*! Converts AMA flag to printable string */ 00625 char *ast_cdr_flags2str(int flag) 00626 { 00627 switch (flag) { 00628 case AST_CDR_OMIT: 00629 return "OMIT"; 00630 case AST_CDR_BILLING: 00631 return "BILLING"; 00632 case AST_CDR_DOCUMENTATION: 00633 return "DOCUMENTATION"; 00634 } 00635 return "Unknown"; 00636 } 00637 00638 int ast_cdr_setaccount(struct ast_channel *chan, const char *account) 00639 { 00640 struct ast_cdr *cdr = chan->cdr; 00641 00642 ast_string_field_set(chan, accountcode, account); 00643 for ( ; cdr ; cdr = cdr->next) { 00644 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 00645 ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode)); 00646 } 00647 return 0; 00648 } 00649 00650 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag) 00651 { 00652 struct ast_cdr *cdr; 00653 int newflag = ast_cdr_amaflags2int(flag); 00654 if (newflag) { 00655 for (cdr = chan->cdr; cdr; cdr = cdr->next) 00656 cdr->amaflags = newflag; 00657 } 00658 00659 return 0; 00660 } 00661 00662 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield) 00663 { 00664 struct ast_cdr *cdr = chan->cdr; 00665 00666 for ( ; cdr ; cdr = cdr->next) { 00667 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 00668 ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield)); 00669 } 00670 00671 return 0; 00672 } 00673 00674 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield) 00675 { 00676 struct ast_cdr *cdr = chan->cdr; 00677 00678 for ( ; cdr ; cdr = cdr->next) { 00679 int len = strlen(cdr->userfield); 00680 00681 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) 00682 ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len); 00683 } 00684 00685 return 0; 00686 } 00687 00688 int ast_cdr_update(struct ast_channel *c) 00689 { 00690 struct ast_cdr *cdr = c->cdr; 00691 00692 for ( ; cdr ; cdr = cdr->next) { 00693 if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00694 set_one_cid(cdr, c); 00695 00696 /* Copy account code et-al */ 00697 ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode)); 00698 /* Destination information */ /* XXX privilege macro* ? */ 00699 ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst)); 00700 ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext)); 00701 } 00702 } 00703 00704 return 0; 00705 } 00706 00707 int ast_cdr_amaflags2int(const char *flag) 00708 { 00709 if (!strcasecmp(flag, "default")) 00710 return 0; 00711 if (!strcasecmp(flag, "omit")) 00712 return AST_CDR_OMIT; 00713 if (!strcasecmp(flag, "billing")) 00714 return AST_CDR_BILLING; 00715 if (!strcasecmp(flag, "documentation")) 00716 return AST_CDR_DOCUMENTATION; 00717 return -1; 00718 } 00719 00720 static void post_cdr(struct ast_cdr *cdr) 00721 { 00722 char *chan; 00723 struct ast_cdr_beitem *i; 00724 00725 for ( ; cdr ; cdr = cdr->next) { 00726 chan = S_OR(cdr->channel, "<unknown>"); 00727 check_post(cdr); 00728 if (ast_tvzero(cdr->end)) 00729 ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan); 00730 if (ast_tvzero(cdr->start)) 00731 ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan); 00732 ast_set_flag(cdr, AST_CDR_FLAG_POSTED); 00733 AST_LIST_LOCK(&be_list); 00734 AST_LIST_TRAVERSE(&be_list, i, list) { 00735 i->be(cdr); 00736 } 00737 AST_LIST_UNLOCK(&be_list); 00738 } 00739 } 00740 00741 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags) 00742 { 00743 struct ast_cdr *dup; 00744 struct ast_flags flags = { 0 }; 00745 00746 if (_flags) 00747 ast_copy_flags(&flags, _flags, AST_FLAGS_ALL); 00748 00749 for ( ; cdr ; cdr = cdr->next) { 00750 /* Detach if post is requested */ 00751 if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) { 00752 if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) { 00753 ast_cdr_end(cdr); 00754 if ((dup = ast_cdr_dup(cdr))) { 00755 ast_cdr_detach(dup); 00756 } 00757 ast_set_flag(cdr, AST_CDR_FLAG_POSTED); 00758 } 00759 00760 /* clear variables */ 00761 if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) { 00762 ast_cdr_free_vars(cdr, 0); 00763 } 00764 00765 /* Reset to initial state */ 00766 ast_clear_flag(cdr, AST_FLAGS_ALL); 00767 memset(&cdr->start, 0, sizeof(cdr->start)); 00768 memset(&cdr->end, 0, sizeof(cdr->end)); 00769 memset(&cdr->answer, 0, sizeof(cdr->answer)); 00770 cdr->billsec = 0; 00771 cdr->duration = 0; 00772 ast_cdr_start(cdr); 00773 cdr->disposition = AST_CDR_NOANSWER; 00774 } 00775 } 00776 } 00777 00778 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr) 00779 { 00780 struct ast_cdr *ret; 00781 00782 if (cdr) { 00783 ret = cdr; 00784 00785 while (cdr->next) 00786 cdr = cdr->next; 00787 cdr->next = newcdr; 00788 } else { 00789 ret = newcdr; 00790 } 00791 00792 return ret; 00793 } 00794 00795 /*! \note Don't call without cdr_batch_lock */ 00796 static void reset_batch(void) 00797 { 00798 batch->size = 0; 00799 batch->head = NULL; 00800 batch->tail = NULL; 00801 } 00802 00803 /*! \note Don't call without cdr_batch_lock */ 00804 static int init_batch(void) 00805 { 00806 /* This is the single meta-batch used to keep track of all CDRs during the entire life of the program */ 00807 if (!(batch = ast_malloc(sizeof(*batch)))) 00808 return -1; 00809 00810 reset_batch(); 00811 00812 return 0; 00813 } 00814 00815 static void *do_batch_backend_process(void *data) 00816 { 00817 struct ast_cdr_batch_item *processeditem; 00818 struct ast_cdr_batch_item *batchitem = data; 00819 00820 /* Push each CDR into storage mechanism(s) and free all the memory */ 00821 while (batchitem) { 00822 post_cdr(batchitem->cdr); 00823 ast_cdr_free(batchitem->cdr); 00824 processeditem = batchitem; 00825 batchitem = batchitem->next; 00826 free(processeditem); 00827 } 00828 00829 return NULL; 00830 } 00831 00832 void ast_cdr_submit_batch(int shutdown) 00833 { 00834 struct ast_cdr_batch_item *oldbatchitems = NULL; 00835 pthread_attr_t attr; 00836 pthread_t batch_post_thread = AST_PTHREADT_NULL; 00837 00838 /* if there's no batch, or no CDRs in the batch, then there's nothing to do */ 00839 if (!batch || !batch->head) 00840 return; 00841 00842 /* move the old CDRs aside, and prepare a new CDR batch */ 00843 ast_mutex_lock(&cdr_batch_lock); 00844 oldbatchitems = batch->head; 00845 reset_batch(); 00846 ast_mutex_unlock(&cdr_batch_lock); 00847 00848 /* if configured, spawn a new thread to post these CDRs, 00849 also try to save as much as possible if we are shutting down safely */ 00850 if (batchscheduleronly || shutdown) { 00851 if (option_debug) 00852 ast_log(LOG_DEBUG, "CDR single-threaded batch processing begins now\n"); 00853 do_batch_backend_process(oldbatchitems); 00854 } else { 00855 pthread_attr_init(&attr); 00856 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00857 if (ast_pthread_create_background(&batch_post_thread, &attr, do_batch_backend_process, oldbatchitems)) { 00858 ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n"); 00859 do_batch_backend_process(oldbatchitems); 00860 } else { 00861 if (option_debug) 00862 ast_log(LOG_DEBUG, "CDR multi-threaded batch processing begins now\n"); 00863 } 00864 pthread_attr_destroy(&attr); 00865 } 00866 } 00867 00868 static int submit_scheduled_batch(void *data) 00869 { 00870 ast_cdr_submit_batch(0); 00871 /* manually reschedule from this point in time */ 00872 cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL); 00873 /* returning zero so the scheduler does not automatically reschedule */ 00874 return 0; 00875 } 00876 00877 static void submit_unscheduled_batch(void) 00878 { 00879 /* this is okay since we are not being called from within the scheduler */ 00880 if (cdr_sched > -1) 00881 ast_sched_del(sched, cdr_sched); 00882 /* schedule the submission to occur ASAP (1 ms) */ 00883 cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL); 00884 /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */ 00885 ast_mutex_lock(&cdr_pending_lock); 00886 ast_cond_signal(&cdr_pending_cond); 00887 ast_mutex_unlock(&cdr_pending_lock); 00888 } 00889 00890 void ast_cdr_detach(struct ast_cdr *cdr) 00891 { 00892 struct ast_cdr_batch_item *newtail; 00893 int curr; 00894 00895 /* maybe they disabled CDR stuff completely, so just drop it */ 00896 if (!enabled) { 00897 if (option_debug) 00898 ast_log(LOG_DEBUG, "Dropping CDR !\n"); 00899 ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED); 00900 ast_cdr_free(cdr); 00901 return; 00902 } 00903 00904 /* post stuff immediately if we are not in batch mode, this is legacy behaviour */ 00905 if (!batchmode) { 00906 post_cdr(cdr); 00907 ast_cdr_free(cdr); 00908 return; 00909 } 00910 00911 /* otherwise, each CDR gets put into a batch list (at the end) */ 00912 if (option_debug) 00913 ast_log(LOG_DEBUG, "CDR detaching from this thread\n"); 00914 00915 /* we'll need a new tail for every CDR */ 00916 if (!(newtail = ast_calloc(1, sizeof(*newtail)))) { 00917 post_cdr(cdr); 00918 ast_cdr_free(cdr); 00919 return; 00920 } 00921 00922 /* don't traverse a whole list (just keep track of the tail) */ 00923 ast_mutex_lock(&cdr_batch_lock); 00924 if (!batch) 00925 init_batch(); 00926 if (!batch->head) { 00927 /* new batch is empty, so point the head at the new tail */ 00928 batch->head = newtail; 00929 } else { 00930 /* already got a batch with something in it, so just append a new tail */ 00931 batch->tail->next = newtail; 00932 } 00933 newtail->cdr = cdr; 00934 batch->tail = newtail; 00935 curr = batch->size++; 00936 ast_mutex_unlock(&cdr_batch_lock); 00937 00938 /* if we have enough stuff to post, then do it */ 00939 if (curr >= (batchsize - 1)) 00940 submit_unscheduled_batch(); 00941 } 00942 00943 static void *do_cdr(void *data) 00944 { 00945 struct timespec timeout; 00946 int schedms; 00947 int numevents = 0; 00948 00949 for (;;) { 00950 struct timeval now; 00951 schedms = ast_sched_wait(sched); 00952 /* this shouldn't happen, but provide a 1 second default just in case */ 00953 if (schedms <= 0) 00954 schedms = 1000; 00955 now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000)); 00956 timeout.tv_sec = now.tv_sec; 00957 timeout.tv_nsec = now.tv_usec * 1000; 00958 /* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */ 00959 ast_mutex_lock(&cdr_pending_lock); 00960 ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout); 00961 numevents = ast_sched_runq(sched); 00962 ast_mutex_unlock(&cdr_pending_lock); 00963 if (