Fri Sep 29 11:12:25 2006

Asterisk developer's documentation


cdr_csv.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * Includes code and algorithms from the Zapata library.
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief Comma Separated Value CDR records.
00024  * 
00025  * \arg See also \ref AstCDR
00026  * \ingroup cdr_drivers
00027  */
00028 
00029 #include <sys/types.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <errno.h>
00033 
00034 #include <stdlib.h>
00035 #include <unistd.h>
00036 #include <time.h>
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
00041 
00042 #include "asterisk/channel.h"
00043 #include "asterisk/cdr.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/logger.h"
00046 #include "asterisk/utils.h"
00047 
00048 #define CSV_LOG_DIR "/cdr-csv"
00049 #define CSV_MASTER  "/Master.csv"
00050 
00051 #define DATE_FORMAT "%Y-%m-%d %T"
00052 
00053 /* #define CSV_LOGUNIQUEID 1 */
00054 /* #define CSV_LOGUSERFIELD 1 */
00055 
00056 /*----------------------------------------------------
00057   The values are as follows:
00058 
00059 
00060   "accountcode",  accountcode is the account name of detail records, Master.csv contains all records *
00061          Detail records are configured on a channel basis, IAX and SIP are determined by user *
00062          Zap is determined by channel in zaptel.conf 
00063   "source",
00064   "destination",
00065   "destination context", 
00066   "callerid",
00067   "channel",
00068   "destination channel",   (if applicable)
00069   "last application",   Last application run on the channel 
00070   "last app argument",  argument to the last channel 
00071   "start time", 
00072   "answer time", 
00073   "end time", 
00074   duration,       Duration is the whole length that the entire call lasted. ie. call rx'd to hangup  
00075          "end time" minus "start time" 
00076   billable seconds,  the duration that a call was up after other end answered which will be <= to duration  
00077          "end time" minus "answer time" 
00078   "disposition",     ANSWERED, NO ANSWER, BUSY 
00079   "amaflags",        DOCUMENTATION, BILL, IGNORE etc, specified on a per channel basis like accountcode. 
00080   "uniqueid",           unique call identifier 
00081   "userfield"     user field set via SetCDRUserField 
00082 ----------------------------------------------------------*/
00083 
00084 static char *desc = "Comma Separated Values CDR Backend";
00085 
00086 static char *name = "csv";
00087 
00088 static FILE *mf = NULL;
00089 
00090 static int append_string(char *buf, char *s, size_t bufsize)
00091 {
00092    int pos = strlen(buf);
00093    int spos = 0;
00094    int error = 0;
00095    if (pos >= bufsize - 4)
00096       return -1;
00097    buf[pos++] = '\"';
00098    error = -1;
00099    while(pos < bufsize - 3) {
00100       if (!s[spos]) {
00101          error = 0;
00102          break;
00103       }
00104       if (s[spos] == '\"')
00105          buf[pos++] = '\"';
00106       buf[pos++] = s[spos];
00107       spos++;
00108    }
00109    buf[pos++] = '\"';
00110    buf[pos++] = ',';
00111    buf[pos++] = '\0';
00112    return error;
00113 }
00114 
00115 static int append_int(char *buf, int s, size_t bufsize)
00116 {
00117    char tmp[32];
00118    int pos = strlen(buf);
00119    snprintf(tmp, sizeof(tmp), "%d", s);
00120    if (pos + strlen(tmp) > bufsize - 3)
00121       return -1;
00122    strncat(buf, tmp, bufsize - strlen(buf) - 1);
00123    pos = strlen(buf);
00124    buf[pos++] = ',';
00125    buf[pos++] = '\0';
00126    return 0;
00127 }
00128 
00129 static int append_date(char *buf, struct timeval tv, size_t bufsize)
00130 {
00131    char tmp[80] = "";
00132    struct tm tm;
00133    time_t t;
00134    t = tv.tv_sec;
00135    if (strlen(buf) > bufsize - 3)
00136       return -1;
00137    if (ast_tvzero(tv)) {
00138       strncat(buf, ",", bufsize - strlen(buf) - 1);
00139       return 0;
00140    }
00141    localtime_r(&t,&tm);
00142    strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm);
00143    return append_string(buf, tmp, bufsize);
00144 }
00145 
00146 static int build_csv_record(char *buf, size_t bufsize, struct ast_cdr *cdr)
00147 {
00148 
00149    buf[0] = '\0';
00150    /* Account code */
00151    append_string(buf, cdr->accountcode, bufsize);
00152    /* Source */
00153    append_string(buf, cdr->src, bufsize);
00154    /* Destination */
00155    append_string(buf, cdr->dst, bufsize);
00156    /* Destination context */
00157    append_string(buf, cdr->dcontext, bufsize);
00158    /* Caller*ID */
00159    append_string(buf, cdr->clid, bufsize);
00160    /* Channel */
00161    append_string(buf, cdr->channel, bufsize);
00162    /* Destination Channel */
00163    append_string(buf, cdr->dstchannel, bufsize);
00164    /* Last Application */
00165    append_string(buf, cdr->lastapp, bufsize);
00166    /* Last Data */
00167    append_string(buf, cdr->lastdata, bufsize);
00168    /* Start Time */
00169    append_date(buf, cdr->start, bufsize);
00170    /* Answer Time */
00171    append_date(buf, cdr->answer, bufsize);
00172    /* End Time */
00173    append_date(buf, cdr->end, bufsize);
00174    /* Duration */
00175    append_int(buf, cdr->duration, bufsize);
00176    /* Billable seconds */
00177    append_int(buf, cdr->billsec, bufsize);
00178    /* Disposition */
00179    append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
00180    /* AMA Flags */
00181    append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
00182 
00183 #ifdef CSV_LOGUNIQUEID
00184    /* Unique ID */
00185    append_string(buf, cdr->uniqueid, bufsize);
00186 #endif
00187 #ifdef CSV_LOGUSERFIELD
00188    /* append the user field */
00189    append_string(buf, cdr->userfield,bufsize);  
00190 #endif
00191    /* If we hit the end of our buffer, log an error */
00192    if (strlen(buf) < bufsize - 5) {
00193       /* Trim off trailing comma */
00194       buf[strlen(buf) - 1] = '\0';
00195       strncat(buf, "\n", bufsize - strlen(buf) - 1);
00196       return 0;
00197    }
00198    return -1;
00199 }
00200 
00201 static int writefile(char *s, char *acc)
00202 {
00203    char tmp[AST_CONFIG_MAX_PATH];
00204    FILE *f;
00205    if (strchr(acc, '/') || (acc[0] == '.')) {
00206       ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc);
00207       return -1;
00208    }
00209    snprintf(tmp, sizeof(tmp), "%s/%s/%s.csv", (char *)ast_config_AST_LOG_DIR,CSV_LOG_DIR, acc);
00210    f = fopen(tmp, "a");
00211    if (!f)
00212       return -1;
00213    fputs(s, f);
00214    fflush(f);
00215    fclose(f);
00216    return 0;
00217 }
00218 
00219 
00220 static int csv_log(struct ast_cdr *cdr)
00221 {
00222    /* Make sure we have a big enough buf */
00223    char buf[1024];
00224    char csvmaster[AST_CONFIG_MAX_PATH];
00225    snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
00226 #if 0
00227    printf("[CDR] %s ('%s' -> '%s') Dur: %ds Bill: %ds Disp: %s Flags: %s Account: [%s]\n", cdr->channel, cdr->src, cdr->dst, cdr->duration, cdr->billsec, ast_cdr_disp2str(cdr->disposition), ast_cdr_flags2str(cdr->amaflags), cdr->accountcode);
00228 #endif
00229    if (build_csv_record(buf, sizeof(buf), cdr)) {
00230       ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes.  CDR not recorded!\n", (int)sizeof(buf));
00231    } else {
00232       /* because of the absolutely unconditional need for the
00233          highest reliability possible in writing billing records,
00234          we open write and close the log file each time */
00235       mf = fopen(csvmaster, "a");
00236       if (!mf) {
00237          ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", csvmaster, strerror(errno));
00238       }
00239       if (mf) {
00240          fputs(buf, mf);
00241          fflush(mf); /* be particularly anal here */
00242          fclose(mf);
00243          mf = NULL;
00244       }
00245       if (!ast_strlen_zero(cdr->accountcode)) {
00246          if (writefile(buf, cdr->accountcode))
00247             ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno));
00248       }
00249    }
00250    return 0;
00251 }
00252 
00253 char *description(void)
00254 {
00255    return desc;
00256 }
00257 
00258 int unload_module(void)
00259 {
00260    if (mf)
00261       fclose(mf);
00262    ast_cdr_unregister(name);
00263    return 0;
00264 }
00265 
00266 int load_module(void)
00267 {
00268    int res;
00269 
00270    res = ast_cdr_register(name, desc, csv_log);
00271    if (res) {
00272       ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n");
00273       if (mf)
00274          fclose(mf);
00275    }
00276    return res;
00277 }
00278 
00279 int reload(void)
00280 {
00281    return 0;
00282 }
00283 
00284 int usecount(void)
00285 {
00286    return 0;
00287 }
00288 
00289 char *key()
00290 {
00291    return ASTERISK_GPL_KEY;
00292 }

Generated on Fri Sep 29 11:12:25 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7