Sat Apr 26 2014 22:02:00

Asterisk developer's documentation


cdr_csv.c File Reference

Comma Separated Value CDR records. More...

#include "asterisk.h"
#include "asterisk/paths.h"
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/cdr.h"
#include "asterisk/module.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
Include dependency graph for cdr_csv.c:

Go to the source code of this file.

Defines

#define CSV_LOG_DIR   "/cdr-csv"
#define CSV_MASTER   "/Master.csv"
#define DATE_FORMAT   "%Y-%m-%d %T"

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int append_date (char *buf, struct timeval when, size_t bufsize)
static int append_int (char *buf, int s, size_t bufsize)
static int append_string (char *buf, const char *s, size_t bufsize)
static int build_csv_record (char *buf, size_t bufsize, struct ast_cdr *cdr)
static int csv_log (struct ast_cdr *cdr)
static int load_config (int reload)
static int load_module (void)
static int reload (void)
static int unload_module (void)
static int writefile (char *s, char *acc)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Comma Separated Values CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, }
static int accountlogs = 1
static ast_mutex_t acf_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static struct ast_module_infoast_module_info = &__mod_info
static const char config [] = "cdr.conf"
static int loaded = 0
static int loguniqueid = 0
static int loguserfield = 0
static ast_mutex_t mf_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 }
static char * name = "csv"
static int usegmtime = 0

Detailed Description

Comma Separated Value CDR records.

Author:
Mark Spencer <markster@digium.com>

Definition in file cdr_csv.c.


Define Documentation

#define CSV_LOG_DIR   "/cdr-csv"

Definition at line 47 of file cdr_csv.c.

Referenced by csv_log(), and writefile().

#define CSV_MASTER   "/Master.csv"

Definition at line 48 of file cdr_csv.c.

Referenced by csv_log().

#define DATE_FORMAT   "%Y-%m-%d %T"

Definition at line 50 of file cdr_csv.c.

Referenced by append_date().


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 356 of file cdr_csv.c.

static void __unreg_module ( void  ) [static]

Definition at line 356 of file cdr_csv.c.

static int append_date ( char *  buf,
struct timeval  when,
size_t  bufsize 
) [static]

Definition at line 179 of file cdr_csv.c.

References append_string(), ast_localtime(), ast_strftime(), ast_tvzero(), and DATE_FORMAT.

Referenced by build_csv_record().

{
   char tmp[80] = "";
   struct ast_tm tm;

   if (strlen(buf) > bufsize - 3)
      return -1;

   if (ast_tvzero(when)) {
      strncat(buf, ",", bufsize - strlen(buf) - 1);
      return 0;
   }

   ast_localtime(&when, &tm, usegmtime ? "GMT" : NULL);
   ast_strftime(tmp, sizeof(tmp), DATE_FORMAT, &tm);

   return append_string(buf, tmp, bufsize);
}
static int append_int ( char *  buf,
int  s,
size_t  bufsize 
) [static]

Definition at line 161 of file cdr_csv.c.

Referenced by build_csv_record().

{
   char tmp[32];
   int pos = strlen(buf);

   snprintf(tmp, sizeof(tmp), "%d", s);

   if (pos + strlen(tmp) > bufsize - 3)
      return -1;

   strncat(buf, tmp, bufsize - strlen(buf) - 1);
   pos = strlen(buf);
   buf[pos++] = ',';
   buf[pos++] = '\0';

   return 0;
}
static int append_string ( char *  buf,
const char *  s,
size_t  bufsize 
) [static]

Definition at line 134 of file cdr_csv.c.

Referenced by append_date(), and build_csv_record().

{
   int pos = strlen(buf), spos = 0, error = -1;

   if (pos >= bufsize - 4)
      return -1;

   buf[pos++] = '\"';

   while(pos < bufsize - 3) {
      if (!s[spos]) {
         error = 0;
         break;
      }
      if (s[spos] == '\"')
         buf[pos++] = '\"';
      buf[pos++] = s[spos];
      spos++;
   }

   buf[pos++] = '\"';
   buf[pos++] = ',';
   buf[pos++] = '\0';

   return error;
}
static int build_csv_record ( char *  buf,
size_t  bufsize,
struct ast_cdr cdr 
) [static]

Definition at line 198 of file cdr_csv.c.

References ast_cdr::accountcode, ast_cdr::amaflags, ast_cdr::answer, append_date(), append_int(), append_string(), ast_cdr_disp2str(), ast_cdr_flags2str(), ast_cdr::billsec, ast_cdr::channel, ast_cdr::clid, ast_cdr::dcontext, ast_cdr::disposition, ast_cdr::dst, ast_cdr::dstchannel, ast_cdr::duration, ast_cdr::end, ast_cdr::lastapp, ast_cdr::lastdata, ast_cdr::src, ast_cdr::start, ast_cdr::uniqueid, and ast_cdr::userfield.

Referenced by csv_log().

{

   buf[0] = '\0';
   /* Account code */
   append_string(buf, cdr->accountcode, bufsize);
   /* Source */
   append_string(buf, cdr->src, bufsize);
   /* Destination */
   append_string(buf, cdr->dst, bufsize);
   /* Destination context */
   append_string(buf, cdr->dcontext, bufsize);
   /* Caller*ID */
   append_string(buf, cdr->clid, bufsize);
   /* Channel */
   append_string(buf, cdr->channel, bufsize);
   /* Destination Channel */
   append_string(buf, cdr->dstchannel, bufsize);
   /* Last Application */
   append_string(buf, cdr->lastapp, bufsize);
   /* Last Data */
   append_string(buf, cdr->lastdata, bufsize);
   /* Start Time */
   append_date(buf, cdr->start, bufsize);
   /* Answer Time */
   append_date(buf, cdr->answer, bufsize);
   /* End Time */
   append_date(buf, cdr->end, bufsize);
   /* Duration */
   append_int(buf, cdr->duration, bufsize);
   /* Billable seconds */
   append_int(buf, cdr->billsec, bufsize);
   /* Disposition */
   append_string(buf, ast_cdr_disp2str(cdr->disposition), bufsize);
   /* AMA Flags */
   append_string(buf, ast_cdr_flags2str(cdr->amaflags), bufsize);
   /* Unique ID */
   if (loguniqueid)
      append_string(buf, cdr->uniqueid, bufsize);
   /* append the user field */
   if(loguserfield)
      append_string(buf, cdr->userfield,bufsize);
   /* If we hit the end of our buffer, log an error */
   if (strlen(buf) < bufsize - 5) {
      /* Trim off trailing comma */
      buf[strlen(buf) - 1] = '\0';
      strncat(buf, "\n", bufsize - strlen(buf) - 1);
      return 0;
   }
   return -1;
}
static int csv_log ( struct ast_cdr cdr) [static]

Definition at line 277 of file cdr_csv.c.

References ast_cdr::accountcode, ast_cdr::amaflags, ast_cdr_disp2str(), ast_cdr_flags2str(), ast_config_AST_LOG_DIR, ast_log(), ast_mutex_lock, ast_mutex_unlock, ast_strlen_zero(), ast_cdr::billsec, build_csv_record(), ast_cdr::channel, CSV_LOG_DIR, CSV_MASTER, ast_cdr::disposition, ast_cdr::dst, ast_cdr::duration, errno, LOG_ERROR, LOG_WARNING, mf_lock, ast_cdr::src, and writefile().

Referenced by load_module().

{
   FILE *mf = NULL;
   /* Make sure we have a big enough buf */
   char buf[1024];
   char csvmaster[PATH_MAX];
   snprintf(csvmaster, sizeof(csvmaster),"%s/%s/%s", ast_config_AST_LOG_DIR, CSV_LOG_DIR, CSV_MASTER);
#if 0
   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);
#endif
   if (build_csv_record(buf, sizeof(buf), cdr)) {
      ast_log(LOG_WARNING, "Unable to create CSV record in %d bytes.  CDR not recorded!\n", (int)sizeof(buf));
      return 0;
   }

   /* because of the absolutely unconditional need for the
      highest reliability possible in writing billing records,
      we open write and close the log file each time */
   ast_mutex_lock(&mf_lock);
   if ((mf = fopen(csvmaster, "a"))) {
      fputs(buf, mf);
      fflush(mf); /* be particularly anal here */
      fclose(mf);
      mf = NULL;
      ast_mutex_unlock(&mf_lock);
   } else {
      ast_mutex_unlock(&mf_lock);
      ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", csvmaster, strerror(errno));
   }

   if (accountlogs && !ast_strlen_zero(cdr->accountcode)) {
      if (writefile(buf, cdr->accountcode))
         ast_log(LOG_WARNING, "Unable to write CSV record to account file '%s' : %s\n", cdr->accountcode, strerror(errno));
   }

   return 0;
}
static int load_config ( int  reload) [static]

Definition at line 95 of file cdr_csv.c.

References ast_config_destroy(), ast_config_load, ast_log(), ast_true(), ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, LOG_WARNING, ast_variable::name, ast_variable::next, and ast_variable::value.

Referenced by load_module(), and reload().

{
   struct ast_config *cfg;
   struct ast_variable *v;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };

   if (!(cfg = ast_config_load(config, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_WARNING, "unable to load config: %s\n", config);
      return 0;
   } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      return 1;
   }

   accountlogs = 1;
   usegmtime = 0;
   loguniqueid = 0;
   loguserfield = 0;

   if (!(v = ast_variable_browse(cfg, "csv"))) {
      ast_config_destroy(cfg);
      return 0;
   }

   for (; v; v = v->next) {
      if (!strcasecmp(v->name, "usegmtime")) {
         usegmtime = ast_true(v->value);
      } else if (!strcasecmp(v->name, "accountlogs")) {
         /* Turn on/off separate files per accountcode. Default is on (as before) */
         accountlogs = ast_true(v->value);
      } else if (!strcasecmp(v->name, "loguniqueid")) {
         loguniqueid = ast_true(v->value);
      } else if (!strcasecmp(v->name, "loguserfield")) {
         loguserfield = ast_true(v->value);
      }
   }
   ast_config_destroy(cfg);
   return 1;
}
static int load_module ( void  ) [static]

Definition at line 322 of file cdr_csv.c.

References ast_cdr_register(), ast_log(), AST_MODULE_LOAD_DECLINE, csv_log(), ast_module_info::description, load_config(), and LOG_ERROR.

{
   int res;

   if (!load_config(0)) {
      return AST_MODULE_LOAD_DECLINE;
   }

   if ((res = ast_cdr_register(name, ast_module_info->description, csv_log))) {
      ast_log(LOG_ERROR, "Unable to register CSV CDR handling\n");
   } else {
      loaded = 1;
   }
   return res;
}
static int reload ( void  ) [static]

Definition at line 338 of file cdr_csv.c.

References ast_cdr_unregister(), ast_log(), load_config(), and LOG_WARNING.

{
   if (load_config(1)) {
      loaded = 1;
   } else {
      loaded = 0;
      ast_log(LOG_WARNING, "No [csv] section in cdr.conf.  Unregistering backend.\n");
      ast_cdr_unregister(name);
   }

   return 0;
}
static int unload_module ( void  ) [static]

Definition at line 315 of file cdr_csv.c.

References ast_cdr_unregister().

{
   ast_cdr_unregister(name);
   loaded = 0;
   return 0;
}
static int writefile ( char *  s,
char *  acc 
) [static]

Definition at line 250 of file cdr_csv.c.

References acf_lock, ast_config_AST_LOG_DIR, ast_log(), ast_mutex_lock, ast_mutex_unlock, CSV_LOG_DIR, errno, f, LOG_ERROR, and LOG_WARNING.

Referenced by csv_log().

{
   char tmp[PATH_MAX];
   FILE *f;

   if (strchr(acc, '/') || (acc[0] == '.')) {
      ast_log(LOG_WARNING, "Account code '%s' insecure for writing file\n", acc);
      return -1;
   }

   snprintf(tmp, sizeof(tmp), "%s/%s/%s.csv", ast_config_AST_LOG_DIR,CSV_LOG_DIR, acc);

   ast_mutex_lock(&acf_lock);
   if (!(f = fopen(tmp, "a"))) {
      ast_mutex_unlock(&acf_lock);
      ast_log(LOG_ERROR, "Unable to open file %s : %s\n", tmp, strerror(errno));
      return -1;
   }
   fputs(s, f);
   fflush(f);
   fclose(f);
   ast_mutex_unlock(&acf_lock);

   return 0;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Comma Separated Values CDR Backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static]

Definition at line 356 of file cdr_csv.c.

int accountlogs = 1 [static]

Definition at line 53 of file cdr_csv.c.

ast_mutex_t acf_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

Definition at line 93 of file cdr_csv.c.

Referenced by writefile().

Definition at line 356 of file cdr_csv.c.

int loaded = 0 [static]

Definition at line 56 of file cdr_csv.c.

int loguniqueid = 0 [static]

Definition at line 54 of file cdr_csv.c.

int loguserfield = 0 [static]

Definition at line 55 of file cdr_csv.c.

ast_mutex_t mf_lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } [static]

Definition at line 92 of file cdr_csv.c.

Referenced by csv_log().

char* name = "csv" [static]

Definition at line 90 of file cdr_csv.c.

int usegmtime = 0 [static]

Definition at line 52 of file cdr_csv.c.

Referenced by load_config().