Mon Jun 27 16:50:49 2011

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

Generated on Mon Jun 27 16:50:49 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7