Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:49 2009

## 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 }