Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:32 2007

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 (