Mon Mar 19 11:30:23 2012

Asterisk developer's documentation


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: 321547 $")
00037 
00038 #include <signal.h>
00039 
00040 #include "asterisk/lock.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/cdr.h"
00043 #include "asterisk/callerid.h"
00044 #include "asterisk/manager.h"
00045 #include "asterisk/causes.h"
00046 #include "asterisk/linkedlists.h"
00047 #include "asterisk/utils.h"
00048 #include "asterisk/sched.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/cli.h"
00051 #include "asterisk/stringfields.h"
00052 #include "asterisk/data.h"
00053 
00054 /*! Default AMA flag for billing records (CDR's) */
00055 int ast_default_amaflags = AST_CDR_DOCUMENTATION;
00056 char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
00057 
00058 struct ast_cdr_beitem {
00059    char name[20];
00060    char desc[80];
00061    ast_cdrbe be;
00062    AST_RWLIST_ENTRY(ast_cdr_beitem) list;
00063 };
00064 
00065 static AST_RWLIST_HEAD_STATIC(be_list, ast_cdr_beitem);
00066 
00067 struct ast_cdr_batch_item {
00068    struct ast_cdr *cdr;
00069    struct ast_cdr_batch_item *next;
00070 };
00071 
00072 static struct ast_cdr_batch {
00073    int size;
00074    struct ast_cdr_batch_item *head;
00075    struct ast_cdr_batch_item *tail;
00076 } *batch = NULL;
00077 
00078 
00079 static int cdr_sequence =  0;
00080 
00081 static int cdr_seq_inc(struct ast_cdr *cdr);
00082 
00083 static struct sched_context *sched;
00084 static int cdr_sched = -1;
00085 static pthread_t cdr_thread = AST_PTHREADT_NULL;
00086 
00087 static int enabled;
00088 static const int ENABLED_DEFAULT = 1;
00089 
00090 static int batchmode;
00091 static const int BATCHMODE_DEFAULT = 0;
00092 
00093 static int unanswered;
00094 static const int UNANSWERED_DEFAULT = 0;
00095 
00096 static int batchsize;
00097 static const int BATCH_SIZE_DEFAULT = 100;
00098 
00099 static int batchtime;
00100 static const int BATCH_TIME_DEFAULT = 300;
00101 
00102 static int batchscheduleronly;
00103 static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
00104 
00105 static int batchsafeshutdown;
00106 static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
00107 
00108 AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
00109 
00110 /* these are used to wake up the CDR thread when there's work to do */
00111 AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
00112 static ast_cond_t cdr_pending_cond;
00113 
00114 int check_cdr_enabled(void)
00115 {
00116    return enabled;
00117 }
00118 
00119 /*!
00120  * \brief Register a CDR driver. Each registered CDR driver generates a CDR
00121  * \retval 0 on success.
00122  * \retval -1 on error
00123  */
00124 int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
00125 {
00126    struct ast_cdr_beitem *i = NULL;
00127 
00128    if (!name)
00129       return -1;
00130 
00131    if (!be) {
00132       ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
00133       return -1;
00134    }
00135 
00136    AST_RWLIST_WRLOCK(&be_list);
00137    AST_RWLIST_TRAVERSE(&be_list, i, list) {
00138       if (!strcasecmp(name, i->name)) {
00139          ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
00140          AST_RWLIST_UNLOCK(&be_list);
00141          return -1;
00142       }
00143    }
00144 
00145    if (!(i = ast_calloc(1, sizeof(*i))))
00146       return -1;
00147 
00148    i->be = be;
00149    ast_copy_string(i->name, name, sizeof(i->name));
00150    ast_copy_string(i->desc, desc, sizeof(i->desc));
00151 
00152    AST_RWLIST_INSERT_HEAD(&be_list, i, list);
00153    AST_RWLIST_UNLOCK(&be_list);
00154 
00155    return 0;
00156 }
00157 
00158 /*! unregister a CDR driver */
00159 void ast_cdr_unregister(const char *name)
00160 {
00161    struct ast_cdr_beitem *i = NULL;
00162 
00163    AST_RWLIST_WRLOCK(&be_list);
00164    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
00165       if (!strcasecmp(name, i->name)) {
00166          AST_RWLIST_REMOVE_CURRENT(list);
00167          break;
00168       }
00169    }
00170    AST_RWLIST_TRAVERSE_SAFE_END;
00171    AST_RWLIST_UNLOCK(&be_list);
00172 
00173    if (i) {
00174       ast_verb(2, "Unregistered '%s' CDR backend\n", name);
00175       ast_free(i);
00176    }
00177 }
00178 
00179 int ast_cdr_isset_unanswered(void)
00180 {
00181    return unanswered;
00182 }
00183 
00184 struct ast_cdr *ast_cdr_dup_unique(struct ast_cdr *cdr)
00185 {
00186    struct ast_cdr *newcdr = ast_cdr_dup(cdr);
00187    if (!newcdr)
00188       return NULL;
00189 
00190    cdr_seq_inc(newcdr);
00191    return newcdr;
00192 }
00193 
00194 struct ast_cdr *ast_cdr_dup_unique_swap(struct ast_cdr *cdr)
00195 {
00196    struct ast_cdr *newcdr = ast_cdr_dup(cdr);
00197    if (!newcdr)
00198       return NULL;
00199 
00200    cdr_seq_inc(cdr);
00201    return newcdr;
00202 }
00203 
00204 /*! Duplicate a CDR record
00205    \returns Pointer to new CDR record
00206 */
00207 struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
00208 {
00209    struct ast_cdr *newcdr;
00210 
00211    if (!cdr) /* don't die if we get a null cdr pointer */
00212       return NULL;
00213    newcdr = ast_cdr_alloc();
00214    if (!newcdr)
00215       return NULL;
00216 
00217    memcpy(newcdr, cdr, sizeof(*newcdr));
00218    /* The varshead is unusable, volatile even, after the memcpy so we take care of that here */
00219    memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
00220    ast_cdr_copy_vars(newcdr, cdr);
00221    newcdr->next = NULL;
00222 
00223    return newcdr;
00224 }
00225 
00226 static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
00227 {
00228    if (ast_strlen_zero(name))
00229       return NULL;
00230 
00231    for (; cdr; cdr = recur ? cdr->next : NULL) {
00232       struct ast_var_t *variables;
00233       struct varshead *headp = &cdr->varshead;
00234       AST_LIST_TRAVERSE(headp, variables, entries) {
00235          if (!strcasecmp(name, ast_var_name(variables)))
00236             return ast_var_value(variables);
00237       }
00238    }
00239 
00240    return NULL;
00241 }
00242 
00243 static void cdr_get_tv(struct timeval when, const char *fmt, char *buf, int bufsize)
00244 {
00245    if (fmt == NULL) {   /* raw mode */
00246       snprintf(buf, bufsize, "%ld.%06ld", (long)when.tv_sec, (long)when.tv_usec);
00247    } else {
00248       if (when.tv_sec) {
00249          struct ast_tm tm;
00250 
00251          ast_localtime(&when, &tm, NULL);
00252          ast_strftime(buf, bufsize, fmt, &tm);
00253       }
00254    }
00255 }
00256 
00257 /*! CDR channel variable retrieval */
00258 void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
00259 {
00260    const char *fmt = "%Y-%m-%d %T";
00261    const char *varbuf;
00262 
00263    if (!cdr)  /* don't die if the cdr is null */
00264       return;
00265 
00266    *ret = NULL;
00267    /* special vars (the ones from the struct ast_cdr when requested by name)
00268       I'd almost say we should convert all the stringed vals to vars */
00269 
00270    if (!strcasecmp(name, "clid"))
00271       ast_copy_string(workspace, cdr->clid, workspacelen);
00272    else if (!strcasecmp(name, "src"))
00273       ast_copy_string(workspace, cdr->src, workspacelen);
00274    else if (!strcasecmp(name, "dst"))
00275       ast_copy_string(workspace, cdr->dst, workspacelen);
00276    else if (!strcasecmp(name, "dcontext"))
00277       ast_copy_string(workspace, cdr->dcontext, workspacelen);
00278    else if (!strcasecmp(name, "channel"))
00279       ast_copy_string(workspace, cdr->channel, workspacelen);
00280    else if (!strcasecmp(name, "dstchannel"))
00281       ast_copy_string(workspace, cdr->dstchannel, workspacelen);
00282    else if (!strcasecmp(name, "lastapp"))
00283       ast_copy_string(workspace, cdr->lastapp, workspacelen);
00284    else if (!strcasecmp(name, "lastdata"))
00285       ast_copy_string(workspace, cdr->lastdata, workspacelen);
00286    else if (!strcasecmp(name, "start"))
00287       cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
00288    else if (!strcasecmp(name, "answer"))
00289       cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
00290    else if (!strcasecmp(name, "end"))
00291       cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
00292    else if (!strcasecmp(name, "duration"))
00293       snprintf(workspace, workspacelen, "%ld", cdr->duration ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
00294    else if (!strcasecmp(name, "billsec"))
00295       snprintf(workspace, workspacelen, "%ld", cdr->billsec || cdr->answer.tv_sec == 0 ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
00296    else if (!strcasecmp(name, "disposition")) {
00297       if (raw) {
00298          snprintf(workspace, workspacelen, "%ld", cdr->disposition);
00299       } else {
00300          ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
00301       }
00302    } else if (!strcasecmp(name, "amaflags")) {
00303       if (raw) {
00304          snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
00305       } else {
00306          ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
00307       }
00308    } else if (!strcasecmp(name, "accountcode"))
00309       ast_copy_string(workspace, cdr->accountcode, workspacelen);
00310    else if (!strcasecmp(name, "peeraccount"))
00311       ast_copy_string(workspace, cdr->peeraccount, workspacelen);
00312    else if (!strcasecmp(name, "uniqueid"))
00313       ast_copy_string(workspace, cdr->uniqueid, workspacelen);
00314    else if (!strcasecmp(name, "linkedid"))
00315       ast_copy_string(workspace, cdr->linkedid, workspacelen);
00316    else if (!strcasecmp(name, "userfield"))
00317       ast_copy_string(workspace, cdr->userfield, workspacelen);
00318    else if (!strcasecmp(name, "sequence"))
00319       snprintf(workspace, workspacelen, "%d", cdr->sequence);
00320    else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
00321       ast_copy_string(workspace, varbuf, workspacelen);
00322    else
00323       workspace[0] = '\0';
00324 
00325    if (!ast_strlen_zero(workspace))
00326       *ret = workspace;
00327 }
00328 
00329 /* readonly cdr variables */
00330 static const char * const cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
00331                     "lastapp", "lastdata", "start", "answer", "end", "duration",
00332                     "billsec", "disposition", "amaflags", "accountcode", "uniqueid", "linkedid",
00333                     "userfield", "sequence", NULL };
00334 /*! Set a CDR channel variable
00335    \note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
00336 */
00337 int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
00338 {
00339    struct ast_var_t *newvariable;
00340    struct varshead *headp;
00341    int x;
00342 
00343    for (x = 0; cdr_readonly_vars[x]; x++) {
00344       if (!strcasecmp(name, cdr_readonly_vars[x])) {
00345          ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
00346          return -1;
00347       }
00348    }
00349 
00350    if (!cdr) {
00351       ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
00352       return -1;
00353    }
00354 
00355    for (; cdr; cdr = recur ? cdr->next : NULL) {
00356       if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00357          continue;
00358       headp = &cdr->varshead;
00359       AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
00360          if (!strcasecmp(ast_var_name(newvariable), name)) {
00361             /* there is already such a variable, delete it */
00362             AST_LIST_REMOVE_CURRENT(entries);
00363             ast_var_delete(newvariable);
00364             break;
00365          }
00366       }
00367       AST_LIST_TRAVERSE_SAFE_END;
00368 
00369       if (value) {
00370          newvariable = ast_var_assign(name, value);
00371          AST_LIST_INSERT_HEAD(headp, newvariable, entries);
00372       }
00373    }
00374 
00375    return 0;
00376 }
00377 
00378 int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
00379 {
00380    struct ast_var_t *variables, *newvariable = NULL;
00381    struct varshead *headpa, *headpb;
00382    const char *var, *val;
00383    int x = 0;
00384 
00385    if (!to_cdr || !from_cdr) /* don't die if one of the pointers is null */
00386       return 0;
00387 
00388    headpa = &from_cdr->varshead;
00389    headpb = &to_cdr->varshead;
00390 
00391    AST_LIST_TRAVERSE(headpa,variables,entries) {
00392       if (variables &&
00393           (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
00394           !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
00395          newvariable = ast_var_assign(var, val);
00396          AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
00397          x++;
00398       }
00399    }
00400 
00401    return x;
00402 }
00403 
00404 int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
00405 {
00406    struct ast_var_t *variables;
00407    const char *var;
00408    char *tmp;
00409    char workspace[256];
00410    int total = 0, x = 0, i;
00411 
00412    ast_str_reset(*buf);
00413 
00414    for (; cdr; cdr = recur ? cdr->next : NULL) {
00415       if (++x > 1)
00416          ast_str_append(buf, 0, "\n");
00417 
00418       AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
00419          if (!(var = ast_var_name(variables))) {
00420             continue;
00421          }
00422 
00423          if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, S_OR(ast_var_value(variables), ""), sep) < 0) {
00424             ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00425             break;
00426          }
00427 
00428          total++;
00429       }
00430 
00431       for (i = 0; cdr_readonly_vars[i]; i++) {
00432          workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */
00433          ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
00434          if (!tmp)
00435             continue;
00436 
00437          if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
00438             ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
00439             break;
00440          } else
00441             total++;
00442       }
00443    }
00444 
00445    return total;
00446 }
00447 
00448 
00449 void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
00450 {
00451 
00452    /* clear variables */
00453    for (; cdr; cdr = recur ? cdr->next : NULL) {
00454       struct ast_var_t *vardata;
00455       struct varshead *headp = &cdr->varshead;
00456       while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
00457          ast_var_delete(vardata);
00458    }
00459 }
00460 
00461 /*! \brief  print a warning if cdr already posted */
00462 static void check_post(struct ast_cdr *cdr)
00463 {
00464    if (!cdr)
00465       return;
00466    if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
00467       ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
00468 }
00469 
00470 void ast_cdr_free(struct ast_cdr *cdr)
00471 {
00472 
00473    while (cdr) {
00474       struct ast_cdr *next = cdr->next;
00475 
00476       ast_cdr_free_vars(cdr, 0);
00477       ast_free(cdr);
00478       cdr = next;
00479    }
00480 }
00481 
00482 /*! \brief the same as a cdr_free call, only with no checks; just get rid of it */
00483 void ast_cdr_discard(struct ast_cdr *cdr)
00484 {
00485    while (cdr) {
00486       struct ast_cdr *next = cdr->next;
00487 
00488       ast_cdr_free_vars(cdr, 0);
00489       ast_free(cdr);
00490       cdr = next;
00491    }
00492 }
00493 
00494 struct ast_cdr *ast_cdr_alloc(void)
00495 {
00496    struct ast_cdr *x;
00497    x = ast_calloc(1, sizeof(*x));
00498    if (!x)
00499       ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
00500    return x;
00501 }
00502 
00503 static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
00504 {
00505    struct ast_var_t *variablesfrom,*variablesto;
00506    struct varshead *headpfrom = &to->varshead;
00507    struct varshead *headpto = &from->varshead;
00508    AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
00509       /* for every var in from, stick it in to */
00510       const char *fromvarname, *fromvarval;
00511       const char *tovarname = NULL, *tovarval = NULL;
00512       fromvarname = ast_var_name(variablesfrom);
00513       fromvarval = ast_var_value(variablesfrom);
00514       tovarname = 0;
00515 
00516       /* now, quick see if that var is in the 'to' cdr already */
00517       AST_LIST_TRAVERSE(headpto, variablesto, entries) {
00518 
00519          /* now, quick see if that var is in the 'to' cdr already */
00520          if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
00521             tovarname = ast_var_name(variablesto);
00522             tovarval = ast_var_value(variablesto);
00523             break;
00524          }
00525       }
00526       if (tovarname && strcasecmp(fromvarval,tovarval) != 0) {  /* this message here to see how irritating the userbase finds it */
00527          ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
00528          continue;
00529       } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0) /* if they are the same, the job is done */
00530          continue;
00531 
00532       /* rip this var out of the from cdr, and stick it in the to cdr */
00533       AST_LIST_MOVE_CURRENT(headpto, entries);
00534    }
00535    AST_LIST_TRAVERSE_SAFE_END;
00536 }
00537 
00538 void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
00539 {
00540    struct ast_cdr *zcdr;
00541    struct ast_cdr *lto = NULL;
00542    struct ast_cdr *lfrom = NULL;
00543    int discard_from = 0;
00544 
00545    if (!to || !from)
00546       return;
00547 
00548    /* don't merge into locked CDR's -- it's bad business */
00549    if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00550       zcdr = to; /* safety valve? */
00551       while (to->next) {
00552          lto = to;
00553          to = to->next;
00554       }
00555 
00556       if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
00557          ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
00558          to = zcdr; /* safety-- if all there are is locked CDR's, then.... ?? */
00559          lto = NULL;
00560       }
00561    }
00562 
00563    if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
00564       struct ast_cdr *llfrom = NULL;
00565       discard_from = 1;
00566       if (lto) {
00567          /* insert the from stuff after lto */
00568          lto->next = from;
00569          lfrom = from;
00570          while (lfrom && lfrom->next) {
00571             if (!lfrom->next->next)
00572                llfrom = lfrom;
00573             lfrom = lfrom->next;
00574          }
00575          /* rip off the last entry and put a copy of the to at the end */
00576          llfrom->next = to;
00577          from = lfrom;
00578       } else {
00579          /* save copy of the current *to cdr */
00580          struct ast_cdr tcdr;
00581          memcpy(&tcdr, to, sizeof(tcdr));
00582          /* copy in the locked from cdr */
00583          memcpy(to, from, sizeof(*to));
00584          lfrom = from;
00585          while (lfrom && lfrom->next) {
00586             if (!lfrom->next->next)
00587                llfrom = lfrom;
00588             lfrom = lfrom->next;
00589          }
00590          from->next = NULL;
00591          /* rip off the last entry and put a copy of the to at the end */
00592          if (llfrom == from)
00593             to = to->next = ast_cdr_dup(&tcdr);
00594          else
00595             to = llfrom->next = ast_cdr_dup(&tcdr);
00596          from = lfrom;
00597       }
00598    }
00599 
00600    if (!ast_tvzero(from->start)) {
00601       if (!ast_tvzero(to->start)) {
00602          if (ast_tvcmp(to->start, from->start) > 0 ) {
00603             to->start = from->start; /* use the earliest time */
00604             from->start = ast_tv(0,0); /* we actively "steal" these values */
00605          }
00606          /* else nothing to do */
00607       } else {
00608          to->start = from->start;
00609          from->start = ast_tv(0,0); /* we actively "steal" these values */
00610       }
00611    }
00612    if (!ast_tvzero(from->answer)) {
00613       if (!ast_tvzero(to->answer)) {
00614          if (ast_tvcmp(to->answer, from->answer) > 0 ) {
00615             to->answer = from->answer; /* use the earliest time */
00616             from->answer = ast_tv(0,0); /* we actively "steal" these values */
00617          }
00618          /* we got the earliest answer time, so we'll settle for that? */
00619       } else {
00620          to->answer = from->answer;
00621          from->answer = ast_tv(0,0); /* we actively "steal" these values */
00622       }
00623    }
00624    if (!ast_tvzero(from->end)) {
00625       if (!ast_tvzero(to->end)) {
00626          if (ast_tvcmp(to->end, from->end) < 0 ) {
00627             to->end = from->end; /* use the latest time */
00628             from->end = ast_tv(0,0); /* we actively "steal" these values */
00629             to->duration = to->end.tv_sec - to->start.tv_sec;  /* don't forget to update the duration, billsec, when we set end */
00630             to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00631          }
00632          /* else, nothing to do */
00633       } else {
00634          to->end = from->end;
00635          from->end = ast_tv(0,0); /* we actively "steal" these values */
00636          to->duration = to->end.tv_sec - to->start.tv_sec;
00637          to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
00638       }
00639    }
00640    if (to->disposition < from->disposition) {
00641       to->disposition = from->disposition;
00642       from->disposition = AST_CDR_NOANSWER;
00643    }
00644    if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
00645       ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
00646       from->lastapp[0] = 0; /* theft */
00647    }
00648    if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
00649       ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
00650       from->lastdata[0] = 0; /* theft */
00651    }
00652    if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
00653       ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
00654       from->dcontext[0] = 0; /* theft */
00655    }
00656    if (ast_strlen_zero(to->dstchannel) && !ast_strlen_zero(from->dstchannel)) {
00657       ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
00658       from->dstchannel[0] = 0; /* theft */
00659    }
00660    if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
00661       ast_copy_string(to->channel, from->channel, sizeof(to->channel));
00662       from->channel[0] = 0; /* theft */
00663    }
00664    if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
00665       ast_copy_string(to->src, from->src, sizeof(to->src));
00666       from->src[0] = 0; /* theft */
00667    }
00668    if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
00669       ast_copy_string(to->clid, from->clid, sizeof(to->clid));
00670       from->clid[0] = 0; /* theft */
00671    }
00672    if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
00673       ast_copy_string(to->dst, from->dst, sizeof(to->dst));
00674       from->dst[0] = 0; /* theft */
00675    }
00676    if (!to->amaflags)
00677       to->amaflags = AST_CDR_DOCUMENTATION;
00678    if (!from->amaflags)
00679       from->amaflags = AST_CDR_DOCUMENTATION; /* make sure both amaflags are set to something (DOC is default) */
00680    if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (to->amaflags == AST_CDR_DOCUMENTATION && from->amaflags != AST_CDR_DOCUMENTATION)) {
00681       to->amaflags = from->amaflags;
00682    }
00683    if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
00684       ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
00685    }
00686    if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->peeraccount) && !ast_strlen_zero(from->peeraccount))) {
00687       ast_copy_string(to->peeraccount, from->peeraccount, sizeof(to->peeraccount));
00688    }
00689    if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
00690       ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
00691    }
00692    /* flags, varsead, ? */
00693    cdr_merge_vars(from, to);
00694 
00695    if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
00696       ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
00697    if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
00698       ast_set_flag(to, AST_CDR_FLAG_POSTED);
00699    if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
00700       ast_set_flag(to, AST_CDR_FLAG_LOCKED);
00701    if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
00702       ast_set_flag(to, AST_CDR_FLAG_CHILD);
00703    if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
00704       ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
00705 
00706    /* last, but not least, we need to merge any forked CDRs to the 'to' cdr */
00707    while (from->next) {
00708       /* just rip 'em off the 'from' and insert them on the 'to' */
00709       zcdr = from->next;
00710       from->next = zcdr->next;
00711       zcdr->next = NULL;
00712       /* zcdr is now ripped from the current list; */
00713       ast_cdr_append(to, zcdr);
00714    }
00715    if (discard_from)
00716       ast_cdr_discard(from);
00717 }
00718 
00719 void ast_cdr_start(struct ast_cdr *cdr)
00720 {
00721    for (; cdr; cdr = cdr->next) {
00722       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00723          check_post(cdr);
00724          cdr->start = ast_tvnow();
00725       }
00726    }
00727 }
00728 
00729 void ast_cdr_answer(struct ast_cdr *cdr)
00730 {
00731 
00732    for (; cdr; cdr = cdr->next) {
00733       if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00734          continue;
00735       if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00736          continue;
00737       check_post(cdr);
00738       if (cdr->disposition < AST_CDR_ANSWERED)
00739          cdr->disposition = AST_CDR_ANSWERED;
00740       if (ast_tvzero(cdr->answer))
00741          cdr->answer = ast_tvnow();
00742    }
00743 }
00744 
00745 void ast_cdr_busy(struct ast_cdr *cdr)
00746 {
00747 
00748    for (; cdr; cdr = cdr->next) {
00749       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00750          check_post(cdr);
00751          cdr->disposition = AST_CDR_BUSY;
00752       }
00753    }
00754 }
00755 
00756 void ast_cdr_failed(struct ast_cdr *cdr)
00757 {
00758    for (; cdr; cdr = cdr->next) {
00759       check_post(cdr);
00760       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00761          check_post(cdr);
00762          if (cdr->disposition < AST_CDR_FAILED)
00763             cdr->disposition = AST_CDR_FAILED;
00764       }
00765    }
00766 }
00767 
00768 void ast_cdr_noanswer(struct ast_cdr *cdr)
00769 {
00770    while (cdr) {
00771       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00772          check_post(cdr);
00773          cdr->disposition = AST_CDR_NOANSWER;
00774       }
00775       cdr = cdr->next;
00776    }
00777 }
00778 
00779 /* everywhere ast_cdr_disposition is called, it will call ast_cdr_failed()
00780    if ast_cdr_disposition returns a non-zero value */
00781 
00782 int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
00783 {
00784    int res = 0;
00785 
00786    for (; cdr; cdr = cdr->next) {
00787       switch (cause) {  /* handle all the non failure, busy cases, return 0 not to set disposition,
00788                      return -1 to set disposition to FAILED */
00789       case AST_CAUSE_BUSY:
00790          ast_cdr_busy(cdr);
00791          break;
00792       case AST_CAUSE_NO_ANSWER:
00793          ast_cdr_noanswer(cdr);
00794          break;
00795       case AST_CAUSE_NORMAL:
00796          break;
00797       default:
00798          res = -1;
00799       }
00800    }
00801    return res;
00802 }
00803 
00804 void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
00805 {
00806    for (; cdr; cdr = cdr->next) {
00807       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00808          check_post(cdr);
00809          ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
00810       }
00811    }
00812 }
00813 
00814 void ast_cdr_setapp(struct ast_cdr *cdr, const char *app, const char *data)
00815 {
00816 
00817    for (; cdr; cdr = cdr->next) {
00818       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00819          check_post(cdr);
00820          ast_copy_string(cdr->lastapp, S_OR(app, ""), sizeof(cdr->lastapp));
00821          ast_copy_string(cdr->lastdata, S_OR(data, ""), sizeof(cdr->lastdata));
00822       }
00823    }
00824 }
00825 
00826 void ast_cdr_setanswer(struct ast_cdr *cdr, struct timeval t)
00827 {
00828 
00829    for (; cdr; cdr = cdr->next) {
00830       if (ast_test_flag(cdr, AST_CDR_FLAG_ANSLOCKED))
00831          continue;
00832       if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00833          continue;
00834       check_post(cdr);
00835       cdr->answer = t;
00836    }
00837 }
00838 
00839 void ast_cdr_setdisposition(struct ast_cdr *cdr, long int disposition)
00840 {
00841 
00842    for (; cdr; cdr = cdr->next) {
00843       if (ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00844          continue;
00845       check_post(cdr);
00846       cdr->disposition = disposition;
00847    }
00848 }
00849 
00850 /* set cid info for one record */
00851 static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
00852 {
00853    const char *num;
00854 
00855    if (!cdr) {
00856       return;
00857    }
00858 
00859    /* Grab source from ANI or normal Caller*ID */
00860    num = S_COR(c->caller.ani.number.valid, c->caller.ani.number.str,
00861       S_COR(c->caller.id.number.valid, c->caller.id.number.str, NULL));
00862    ast_callerid_merge(cdr->clid, sizeof(cdr->clid),
00863       S_COR(c->caller.id.name.valid, c->caller.id.name.str, NULL), num, "");
00864    ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
00865    ast_cdr_setvar(cdr, "dnid", S_OR(c->dialed.number.str, ""), 0);
00866 
00867    if (c->caller.id.subaddress.valid) {
00868       ast_cdr_setvar(cdr, "callingsubaddr", S_OR(c->caller.id.subaddress.str, ""), 0);
00869    }
00870    if (c->dialed.subaddress.valid) {
00871       ast_cdr_setvar(cdr, "calledsubaddr", S_OR(c->dialed.subaddress.str, ""), 0);
00872    }
00873 }
00874 
00875 int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
00876 {
00877    for (; cdr; cdr = cdr->next) {
00878       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00879          set_one_cid(cdr, c);
00880    }
00881    return 0;
00882 }
00883 
00884 static int cdr_seq_inc(struct ast_cdr *cdr)
00885 {
00886    return (cdr->sequence = ast_atomic_fetchadd_int(&cdr_sequence, +1));
00887 }
00888 
00889 int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
00890 {
00891    for ( ; cdr ; cdr = cdr->next) {
00892       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00893          ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
00894          set_one_cid(cdr, c);
00895          cdr_seq_inc(cdr);
00896 
00897          cdr->disposition = (c->_state == AST_STATE_UP) ?  AST_CDR_ANSWERED : AST_CDR_NOANSWER;
00898          cdr->amaflags = c->amaflags ? c->amaflags :  ast_default_amaflags;
00899          ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
00900          ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
00901          /* Destination information */
00902          ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
00903          ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
00904          /* Unique call identifier */
00905          ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
00906          /* Linked call identifier */
00907          ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
00908       }
00909    }
00910    return 0;
00911 }
00912 
00913 /* Three routines were "fixed" via 10668, and later shown that
00914    users were depending on this behavior. ast_cdr_end,
00915    ast_cdr_setvar and ast_cdr_answer are the three routines.
00916    While most of the other routines would not touch
00917    LOCKED cdr's, these three routines were designed to
00918    operate on locked CDR's as a matter of course.
00919    I now appreciate how this plays with the ForkCDR app,
00920    which forms these cdr chains in the first place.
00921    cdr_end is pretty key: all cdrs created are closed
00922    together. They only vary by start time. Arithmetically,
00923    users can calculate the subintervals they wish to track. */
00924 
00925 void ast_cdr_end(struct ast_cdr *cdr)
00926 {
00927    for ( ; cdr ; cdr = cdr->next) {
00928       if (ast_test_flag(cdr, AST_CDR_FLAG_DONT_TOUCH) && ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
00929          continue;
00930       check_post(cdr);
00931       if (ast_tvzero(cdr->end))
00932          cdr->end = ast_tvnow();
00933       if (ast_tvzero(cdr->start)) {
00934          ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
00935          cdr->disposition = AST_CDR_FAILED;
00936       } else
00937          cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
00938       if (ast_tvzero(cdr->answer)) {
00939          if (cdr->disposition == AST_CDR_ANSWERED) {
00940             ast_log(LOG_WARNING, "CDR on channel '%s' has no answer time but is 'ANSWERED'\n", S_OR(cdr->channel, "<unknown>"));
00941             cdr->disposition = AST_CDR_FAILED;
00942          }
00943       } else {
00944          cdr->billsec = cdr->end.tv_sec - cdr->answer.tv_sec;
00945          if (ast_test_flag(&ast_options, AST_OPT_FLAG_INITIATED_SECONDS))
00946             cdr->billsec += cdr->end.tv_usec > cdr->answer.tv_usec ? 1 : 0;
00947       }
00948    }
00949 }
00950 
00951 char *ast_cdr_disp2str(int disposition)
00952 {
00953    switch (disposition) {
00954    case AST_CDR_NULL:
00955       return "NO ANSWER"; /* by default, for backward compatibility */
00956    case AST_CDR_NOANSWER:
00957       return "NO ANSWER";
00958    case AST_CDR_FAILED:
00959       return "FAILED";
00960    case AST_CDR_BUSY:
00961       return "BUSY";
00962    case AST_CDR_ANSWERED:
00963       return "ANSWERED";
00964    }
00965    return "UNKNOWN";
00966 }
00967 
00968 /*! Converts AMA flag to printable string */
00969 char *ast_cdr_flags2str(int flag)
00970 {
00971    switch (flag) {
00972    case AST_CDR_OMIT:
00973       return "OMIT";
00974    case AST_CDR_BILLING:
00975       return "BILLING";
00976    case AST_CDR_DOCUMENTATION:
00977       return "DOCUMENTATION";
00978    }
00979    return "Unknown";
00980 }
00981 
00982 int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
00983 {
00984    struct ast_cdr *cdr = chan->cdr;
00985    const char *old_acct = "";
00986 
00987    if (!ast_strlen_zero(chan->accountcode)) {
00988       old_acct = ast_strdupa(chan->accountcode);
00989    }
00990 
00991    ast_string_field_set(chan, accountcode, account);
00992    for ( ; cdr ; cdr = cdr->next) {
00993       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
00994          ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
00995       }
00996    }
00997 
00998    ast_manager_event(chan, EVENT_FLAG_CALL, "NewAccountCode",
00999          "Channel: %s\r\n"
01000          "Uniqueid: %s\r\n"
01001          "AccountCode: %s\r\n"
01002          "OldAccountCode: %s\r\n",
01003          chan->name, chan->uniqueid, chan->accountcode, old_acct);
01004 
01005    return 0;
01006 }
01007 
01008 int ast_cdr_setpeeraccount(struct ast_channel *chan, const char *account)
01009 {
01010    struct ast_cdr *cdr = chan->cdr;
01011    const char *old_acct = "";
01012 
01013    if (!ast_strlen_zero(chan->peeraccount)) {
01014       old_acct = ast_strdupa(chan->peeraccount);
01015    }
01016 
01017    ast_string_field_set(chan, peeraccount, account);
01018    for ( ; cdr ; cdr = cdr->next) {
01019       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01020          ast_copy_string(cdr->peeraccount, chan->peeraccount, sizeof(cdr->peeraccount));
01021       }
01022    }
01023 
01024    ast_manager_event(chan, EVENT_FLAG_CALL, "NewPeerAccount",
01025          "Channel: %s\r\n"
01026          "Uniqueid: %s\r\n"
01027          "PeerAccount: %s\r\n"
01028          "OldPeerAccount: %s\r\n",
01029          chan->name, chan->uniqueid, chan->peeraccount, old_acct);
01030 
01031    return 0;
01032 }
01033 
01034 int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
01035 {
01036    struct ast_cdr *cdr;
01037    int newflag = ast_cdr_amaflags2int(flag);
01038    if (newflag) {
01039       for (cdr = chan->cdr; cdr; cdr = cdr->next) {
01040          if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01041             cdr->amaflags = newflag;
01042          }
01043       }
01044    }
01045 
01046    return 0;
01047 }
01048 
01049 int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
01050 {
01051    struct ast_cdr *cdr = chan->cdr;
01052 
01053    for ( ; cdr ; cdr = cdr->next) {
01054       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
01055          ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
01056    }
01057 
01058    return 0;
01059 }
01060 
01061 int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
01062 {
01063    struct ast_cdr *cdr = chan->cdr;
01064 
01065    for ( ; cdr ; cdr = cdr->next) {
01066       int len = strlen(cdr->userfield);
01067 
01068       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
01069          ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
01070    }
01071 
01072    return 0;
01073 }
01074 
01075 int ast_cdr_update(struct ast_channel *c)
01076 {
01077    struct ast_cdr *cdr = c->cdr;
01078 
01079    for ( ; cdr ; cdr = cdr->next) {
01080       if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01081          set_one_cid(cdr, c);
01082 
01083          /* Copy account code et-al */
01084          ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
01085          ast_copy_string(cdr->peeraccount, c->peeraccount, sizeof(cdr->peeraccount));
01086          ast_copy_string(cdr->linkedid, c->linkedid, sizeof(cdr->linkedid));
01087 
01088          /* Destination information */ /* XXX privilege macro* ? */
01089          ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
01090          ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
01091       }
01092    }
01093 
01094    return 0;
01095 }
01096 
01097 int ast_cdr_amaflags2int(const char *flag)
01098 {
01099    if (!strcasecmp(flag, "default"))
01100       return 0;
01101    if (!strcasecmp(flag, "omit"))
01102       return AST_CDR_OMIT;
01103    if (!strcasecmp(flag, "billing"))
01104       return AST_CDR_BILLING;
01105    if (!strcasecmp(flag, "documentation"))
01106       return AST_CDR_DOCUMENTATION;
01107    return -1;
01108 }
01109 
01110 static void post_cdr(struct ast_cdr *cdr)
01111 {
01112    struct ast_cdr_beitem *i;
01113 
01114    for ( ; cdr ; cdr = cdr->next) {
01115       if (!unanswered && cdr->disposition < AST_CDR_ANSWERED && (ast_strlen_zero(cdr->channel) || ast_strlen_zero(cdr->dstchannel))) {
01116          /* For people, who don't want to see unanswered single-channel events */
01117          ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01118          continue;
01119       }
01120 
01121       /* don't post CDRs that are for dialed channels unless those
01122        * channels were originated from asterisk (pbx_spool, manager,
01123        * cli) */
01124       if (ast_test_flag(cdr, AST_CDR_FLAG_DIALED) && !ast_test_flag(cdr, AST_CDR_FLAG_ORIGINATED)) {
01125          ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01126          continue;
01127       }
01128 
01129       check_post(cdr);
01130       ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01131       if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
01132          continue;
01133       AST_RWLIST_RDLOCK(&be_list);
01134       AST_RWLIST_TRAVERSE(&be_list, i, list) {
01135          i->be(cdr);
01136       }
01137       AST_RWLIST_UNLOCK(&be_list);
01138    }
01139 }
01140 
01141 void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01142 {
01143    struct ast_cdr *duplicate;
01144    struct ast_flags flags = { 0 };
01145 
01146    if (_flags)
01147       ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01148 
01149    for ( ; cdr ; cdr = cdr->next) {
01150       /* Detach if post is requested */
01151       if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
01152          if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
01153             ast_cdr_end(cdr);
01154             if ((duplicate = ast_cdr_dup_unique_swap(cdr))) {
01155                ast_cdr_detach(duplicate);
01156             }
01157             ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
01158          }
01159 
01160          /* enable CDR only */
01161          if (ast_test_flag(&flags, AST_CDR_FLAG_POST_ENABLE)) {
01162             ast_clear_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01163             continue;
01164          }
01165 
01166          /* clear variables */
01167          if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
01168             ast_cdr_free_vars(cdr, 0);
01169          }
01170 
01171          /* Reset to initial state */
01172          ast_clear_flag(cdr, AST_FLAGS_ALL);
01173          memset(&cdr->start, 0, sizeof(cdr->start));
01174          memset(&cdr->end, 0, sizeof(cdr->end));
01175          memset(&cdr->answer, 0, sizeof(cdr->answer));
01176          cdr->billsec = 0;
01177          cdr->duration = 0;
01178          ast_cdr_start(cdr);
01179          cdr->disposition = AST_CDR_NOANSWER;
01180       }
01181    }
01182 }
01183 
01184 void ast_cdr_specialized_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
01185 {
01186    struct ast_flags flags = { 0 };
01187 
01188    if (_flags)
01189       ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
01190 
01191    /* Reset to initial state */
01192    if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED)) { /* But do NOT lose the NoCDR() setting */
01193       ast_clear_flag(cdr, AST_FLAGS_ALL);
01194       ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01195    } else {
01196       ast_clear_flag(cdr, AST_FLAGS_ALL);
01197    }
01198 
01199    memset(&cdr->start, 0, sizeof(cdr->start));
01200    memset(&cdr->end, 0, sizeof(cdr->end));
01201    memset(&cdr->answer, 0, sizeof(cdr->answer));
01202    cdr->billsec = 0;
01203    cdr->duration = 0;
01204    ast_cdr_start(cdr);
01205    cdr->disposition = AST_CDR_NULL;
01206 }
01207 
01208 struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
01209 {
01210    struct ast_cdr *ret;
01211 
01212    if (cdr) {
01213       ret = cdr;
01214 
01215       while (cdr->next)
01216          cdr = cdr->next;
01217       cdr->next = newcdr;
01218    } else {
01219       ret = newcdr;
01220    }
01221 
01222    return ret;
01223 }
01224 
01225 /*! \note Don't call without cdr_batch_lock */
01226 static void reset_batch(void)
01227 {
01228    batch->size = 0;
01229    batch->head = NULL;
01230    batch->tail = NULL;
01231 }
01232 
01233 /*! \note Don't call without cdr_batch_lock */
01234 static int init_batch(void)
01235 {
01236    /* This is the single meta-batch used to keep track of all CDRs during the entire life of the program */
01237    if (!(batch = ast_malloc(sizeof(*batch))))
01238       return -1;
01239 
01240    reset_batch();
01241 
01242    return 0;
01243 }
01244 
01245 static void *do_batch_backend_process(void *data)
01246 {
01247    struct ast_cdr_batch_item *processeditem;
01248    struct ast_cdr_batch_item *batchitem = data;
01249 
01250    /* Push each CDR into storage mechanism(s) and free all the memory */
01251    while (batchitem) {
01252       post_cdr(batchitem->cdr);
01253       ast_cdr_free(batchitem->cdr);
01254       processeditem = batchitem;
01255       batchitem = batchitem->next;
01256       ast_free(processeditem);
01257    }
01258 
01259    return NULL;
01260 }
01261 
01262 void ast_cdr_submit_batch(int do_shutdown)
01263 {
01264    struct ast_cdr_batch_item *oldbatchitems = NULL;
01265    pthread_t batch_post_thread = AST_PTHREADT_NULL;
01266 
01267    /* if there's no batch, or no CDRs in the batch, then there's nothing to do */
01268    if (!batch || !batch->head)
01269       return;
01270 
01271    /* move the old CDRs aside, and prepare a new CDR batch */
01272    ast_mutex_lock(&cdr_batch_lock);
01273    oldbatchitems = batch->head;
01274    reset_batch();
01275    ast_mutex_unlock(&cdr_batch_lock);
01276 
01277    /* if configured, spawn a new thread to post these CDRs,
01278       also try to save as much as possible if we are shutting down safely */
01279    if (batchscheduleronly || do_shutdown) {
01280       ast_debug(1, "CDR single-threaded batch processing begins now\n");
01281       do_batch_backend_process(oldbatchitems);
01282    } else {
01283       if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
01284          ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
01285          do_batch_backend_process(oldbatchitems);
01286       } else {
01287          ast_debug(1, "CDR multi-threaded batch processing begins now\n");
01288       }
01289    }
01290 }
01291 
01292 static int submit_scheduled_batch(const void *data)
01293 {
01294    ast_cdr_submit_batch(0);
01295    /* manually reschedule from this point in time */
01296    cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01297    /* returning zero so the scheduler does not automatically reschedule */
01298    return 0;
01299 }
01300 
01301 static void submit_unscheduled_batch(void)
01302 {
01303    /* this is okay since we are not being called from within the scheduler */
01304    AST_SCHED_DEL(sched, cdr_sched);
01305    /* schedule the submission to occur ASAP (1 ms) */
01306    cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
01307    /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
01308    ast_mutex_lock(&cdr_pending_lock);
01309    ast_cond_signal(&cdr_pending_cond);
01310    ast_mutex_unlock(&cdr_pending_lock);
01311 }
01312 
01313 void ast_cdr_detach(struct ast_cdr *cdr)
01314 {
01315    struct ast_cdr_batch_item *newtail;
01316    int curr;
01317 
01318    if (!cdr)
01319       return;
01320 
01321    /* maybe they disabled CDR stuff completely, so just drop it */
01322    if (!enabled) {
01323       ast_debug(1, "Dropping CDR !\n");
01324       ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
01325       ast_cdr_free(cdr);
01326       return;
01327    }
01328 
01329    /* post stuff immediately if we are not in batch mode, this is legacy behaviour */
01330    if (!batchmode) {
01331       post_cdr(cdr);
01332       ast_cdr_free(cdr);
01333       return;
01334    }
01335 
01336    /* otherwise, each CDR gets put into a batch list (at the end) */
01337    ast_debug(1, "CDR detaching from this thread\n");
01338 
01339    /* we'll need a new tail for every CDR */
01340    if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
01341       post_cdr(cdr);
01342       ast_cdr_free(cdr);
01343       return;
01344    }
01345 
01346    /* don't traverse a whole list (just keep track of the tail) */
01347    ast_mutex_lock(&cdr_batch_lock);
01348    if (!batch)
01349       init_batch();
01350    if (!batch->head) {
01351       /* new batch is empty, so point the head at the new tail */
01352       batch->head = newtail;
01353    } else {
01354       /* already got a batch with something in it, so just append a new tail */
01355       batch->tail->next = newtail;
01356    }
01357    newtail->cdr = cdr;
01358    batch->tail = newtail;
01359    curr = batch->size++;
01360    ast_mutex_unlock(&cdr_batch_lock);
01361 
01362    /* if we have enough stuff to post, then do it */
01363    if (curr >= (batchsize - 1))
01364       submit_unscheduled_batch();
01365 }
01366 
01367 static void *do_cdr(void *data)
01368 {
01369    struct timespec timeout;
01370    int schedms;
01371    int numevents = 0;
01372 
01373    for (;;) {
01374       struct timeval now;
01375       schedms = ast_sched_wait(sched);
01376       /* this shouldn't happen, but provide a 1 second default just in case */
01377       if (schedms <= 0)
01378          schedms = 1000;
01379       now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
01380       timeout.tv_sec = now.tv_sec;
01381       timeout.tv_nsec = now.tv_usec * 1000;
01382       /* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */
01383       ast_mutex_lock(&cdr_pending_lock);
01384       ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
01385       numevents = ast_sched_runq(sched);
01386       ast_mutex_unlock(&cdr_pending_lock);
01387       ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
01388    }
01389 
01390    return NULL;
01391 }
01392 
01393 static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01394 {
01395    struct ast_cdr_beitem *beitem=NULL;
01396    int cnt=0;
01397    long nextbatchtime=0;
01398 
01399    switch (cmd) {
01400    case CLI_INIT:
01401       e->command = "cdr show status";
01402       e->usage =
01403          "Usage: cdr show status\n"
01404          "  Displays the Call Detail Record engine system status.\n";
01405       return NULL;
01406    case CLI_GENERATE:
01407       return NULL;
01408    }
01409 
01410    if (a->argc > 3)
01411       return CLI_SHOWUSAGE;
01412 
01413    ast_cli(a->fd, "\n");
01414    ast_cli(a->fd, "Call Detail Record (CDR) settings\n");
01415    ast_cli(a->fd, "----------------------------------\n");
01416    ast_cli(a->fd, "  Logging:                    %s\n", enabled ? "Enabled" : "Disabled");
01417    ast_cli(a->fd, "  Mode:                       %s\n", batchmode ? "Batch" : "Simple");
01418    if (enabled) {
01419       ast_cli(a->fd, "  Log unanswered calls:       %s\n\n", unanswered ? "Yes" : "No");
01420       if (batchmode) {
01421          ast_cli(a->fd, "* Batch Mode Settings\n");
01422          ast_cli(a->fd, "  -------------------\n");
01423          if (batch)
01424             cnt = batch->size;
01425          if (cdr_sched > -1)
01426             nextbatchtime = ast_sched_when(sched, cdr_sched);
01427          ast_cli(a->fd, "  Safe shutdown:              %s\n", batchsafeshutdown ? "Enabled" : "Disabled");
01428          ast_cli(a->fd, "  Threading model:            %s\n", batchscheduleronly ? "Scheduler only" : "Scheduler plus separate threads");
01429          ast_cli(a->fd, "  Current batch size:         %d record%s\n", cnt, ESS(cnt));
01430          ast_cli(a->fd, "  Maximum batch size:         %d record%s\n", batchsize, ESS(batchsize));
01431          ast_cli(a->fd, "  Maximum batch time:         %d second%s\n", batchtime, ESS(batchtime));
01432          ast_cli(a->fd, "  Next batch processing time: %ld second%s\n\n", nextbatchtime, ESS(nextbatchtime));
01433       }
01434       ast_cli(a->fd, "* Registered Backends\n");
01435       ast_cli(a->fd, "  -------------------\n");
01436       AST_RWLIST_RDLOCK(&be_list);
01437       if (AST_RWLIST_EMPTY(&be_list)) {
01438          ast_cli(a->fd, "    (none)\n");
01439       } else {
01440          AST_RWLIST_TRAVERSE(&be_list, beitem, list) {
01441             ast_cli(a->fd, "    %s\n", beitem->name);
01442          }
01443       }
01444       AST_RWLIST_UNLOCK(&be_list);
01445       ast_cli(a->fd, "\n");
01446    }
01447 
01448    return CLI_SUCCESS;
01449 }
01450 
01451 static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01452 {
01453    switch (cmd) {
01454    case CLI_INIT:
01455       e->command = "cdr submit";
01456       e->usage =
01457          "Usage: cdr submit\n"
01458          "       Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
01459       return NULL;
01460    case CLI_GENERATE:
01461       return NULL;
01462    }
01463    if (a->argc > 2)
01464       return CLI_SHOWUSAGE;
01465 
01466    submit_unscheduled_batch();
01467    ast_cli(a->fd, "Submitted CDRs to backend engines for processing.  This may take a while.\n");
01468 
01469    return CLI_SUCCESS;
01470 }
01471 
01472 static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
01473 static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
01474 
01475 static int do_reload(int reload)
01476 {
01477    struct ast_config *config;
01478    const char *enabled_value;
01479    const char *unanswered_value;
01480    const char *batched_value;
01481    const char *scheduleronly_value;
01482    const char *batchsafeshutdown_value;
01483    const char *size_value;
01484    const char *time_value;
01485    const char *end_before_h_value;
01486    const char *initiatedseconds_value;
01487    int cfg_size;
01488    int cfg_time;
01489    int was_enabled;
01490    int was_batchmode;
01491    int res=0;
01492    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01493 
01494    if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
01495       return 0;
01496    }
01497 
01498    ast_mutex_lock(&cdr_batch_lock);
01499 
01500    was_enabled = enabled;
01501    was_batchmode = batchmode;
01502 
01503    batchsize = BATCH_SIZE_DEFAULT;
01504    batchtime = BATCH_TIME_DEFAULT;
01505    batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
01506    batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
01507    enabled = ENABLED_DEFAULT;
01508    batchmode = BATCHMODE_DEFAULT;
01509    unanswered = UNANSWERED_DEFAULT;
01510 
01511    if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
01512       ast_mutex_unlock(&cdr_batch_lock);
01513       return 0;
01514    }
01515 
01516    /* don't run the next scheduled CDR posting while reloading */
01517    AST_SCHED_DEL(sched, cdr_sched);
01518 
01519    if (config) {
01520       if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
01521          enabled = ast_true(enabled_value);
01522       }
01523       if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
01524          unanswered = ast_true(unanswered_value);
01525       }
01526       if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
01527          batchmode = ast_true(batched_value);
01528       }
01529       if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
01530          batchscheduleronly = ast_true(scheduleronly_value);
01531       }
01532       if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
01533          batchsafeshutdown = ast_true(batchsafeshutdown_value);
01534       }
01535       if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
01536          if (sscanf(size_value, "%30d", &cfg_size) < 1)
01537             ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
01538          else if (cfg_size < 0)
01539             ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
01540          else
01541             batchsize = cfg_size;
01542       }
01543       if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
01544          if (sscanf(time_value, "%30d", &cfg_time) < 1)
01545             ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
01546          else if (cfg_time < 0)
01547             ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
01548          else
01549             batchtime = cfg_time;
01550       }
01551       if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
01552          ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
01553       if ((initiatedseconds_value = ast_variable_retrieve(config, "general", "initiatedseconds")))
01554          ast_set2_flag(&ast_options, ast_true(initiatedseconds_value), AST_OPT_FLAG_INITIATED_SECONDS);
01555    }
01556 
01557    if (enabled && !batchmode) {
01558       ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
01559    } else if (enabled && batchmode) {
01560       cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
01561       ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
01562    } else {
01563       ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
01564    }
01565 
01566    /* if this reload enabled the CDR batch mode, create the background thread
01567       if it does not exist */
01568    if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
01569       ast_cond_init(&cdr_pending_cond, NULL);
01570       if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
01571          ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
01572          AST_SCHED_DEL(sched, cdr_sched);
01573       } else {
01574          ast_cli_register(&cli_submit);
01575          ast_register_atexit(ast_cdr_engine_term);
01576          res = 0;
01577       }
01578    /* if this reload disabled the CDR and/or batch mode and there is a background thread,
01579       kill it */
01580    } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
01581       /* wake up the thread so it will exit */
01582       pthread_cancel(cdr_thread);
01583       pthread_kill(cdr_thread, SIGURG);
01584       pthread_join(cdr_thread, NULL);
01585       cdr_thread = AST_PTHREADT_NULL;
01586       ast_cond_destroy(&cdr_pending_cond);
01587       ast_cli_unregister(&cli_submit);
01588       ast_unregister_atexit(ast_cdr_engine_term);
01589       res = 0;
01590       /* if leaving batch mode, then post the CDRs in the batch,
01591          and don't reschedule, since we are stopping CDR logging */
01592       if (!batchmode && was_batchmode) {
01593          ast_cdr_engine_term();
01594       }
01595    } else {
01596       res = 0;
01597    }
01598 
01599    ast_mutex_unlock(&cdr_batch_lock);
01600    ast_config_destroy(config);
01601    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
01602 
01603    return res;
01604 }
01605 
01606 int ast_cdr_engine_init(void)
01607 {
01608    int res;
01609 
01610    sched = sched_context_create();
01611    if (!sched) {
01612       ast_log(LOG_ERROR, "Unable to create schedule context.\n");
01613       return -1;
01614    }
01615 
01616    ast_cli_register(&cli_status);
01617 
01618    res = do_reload(0);
01619    if (res) {
01620       ast_mutex_lock(&cdr_batch_lock);
01621       res = init_batch();
01622       ast_mutex_unlock(&cdr_batch_lock);
01623    }
01624 
01625    return res;
01626 }
01627 
01628 /* \note This actually gets called a couple of times at shutdown.  Once, before we start
01629    hanging up channels, and then again, after the channel hangup timeout expires */
01630 void ast_cdr_engine_term(void)
01631 {
01632    ast_cdr_submit_batch(batchsafeshutdown);
01633 }
01634 
01635 int ast_cdr_engine_reload(void)
01636 {
01637    return do_reload(1);
01638 }
01639 
01640 int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur)
01641 {
01642    struct ast_cdr *tmpcdr;
01643    struct ast_data *level;
01644    struct ast_var_t *variables;
01645    const char *var, *val;
01646    int x = 1, i;
01647    char workspace[256];
01648    char *tmp;
01649 
01650    if (!cdr) {
01651       return -1;
01652    }
01653 
01654    for (tmpcdr = cdr; tmpcdr; tmpcdr = (recur ? tmpcdr->next : NULL)) {
01655       level = ast_data_add_node(tree, "level");
01656       if (!level) {
01657          continue;
01658       }
01659 
01660       ast_data_add_int(level, "level_number", x);
01661 
01662       AST_LIST_TRAVERSE(&tmpcdr->varshead, variables, entries) {
01663          if (variables && (var = ast_var_name(variables)) &&
01664                (val = ast_var_value(variables)) && !ast_strlen_zero(var)
01665                && !ast_strlen_zero(val)) {
01666             ast_data_add_str(level, var, val);
01667          } else {
01668             break;
01669          }
01670       }
01671 
01672       for (i = 0; cdr_readonly_vars[i]; i++) {
01673          workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */
01674          ast_cdr_getvar(tmpcdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
01675          if (!tmp) {
01676             continue;
01677          }
01678          ast_data_add_str(level, cdr_readonly_vars[i], tmp);
01679       }
01680 
01681       x++;
01682    }
01683 
01684    return 0;
01685 }
01686 

Generated on Mon Mar 19 11:30:23 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7