cdr_custom.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2009, 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 /*!
00022  * \file
00023  * \brief Custom Comma Separated Value CDR records.
00024  *
00025  * \author Mark Spencer <markster@digium.com>
00026  *
00027  * \arg See also \ref AstCDR
00028  *
00029  * Logs in LOG_DIR/cdr_custom
00030  * \ingroup cdr_drivers
00031  */
00032 
00033 /*! \li \ref cdr_custom.c uses the configuration file \ref cdr_custom.conf
00034  * \addtogroup configuration_file Configuration Files
00035  */
00036 
00037 /*!
00038  * \page cdr_custom.conf cdr_custom.conf
00039  * \verbinclude cdr_custom.conf.sample
00040  */
00041 
00042 /*** MODULEINFO
00043    <support_level>core</support_level>
00044  ***/
00045 
00046 #include "asterisk.h"
00047 
00048 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
00049 
00050 #include <time.h>
00051 
00052 #include "asterisk/paths.h"   /* use ast_config_AST_LOG_DIR */
00053 #include "asterisk/channel.h"
00054 #include "asterisk/cdr.h"
00055 #include "asterisk/module.h"
00056 #include "asterisk/config.h"
00057 #include "asterisk/pbx.h"
00058 #include "asterisk/utils.h"
00059 #include "asterisk/lock.h"
00060 #include "asterisk/threadstorage.h"
00061 #include "asterisk/strings.h"
00062 
00063 #define CUSTOM_LOG_DIR "/cdr_custom"
00064 #define CONFIG         "cdr_custom.conf"
00065 
00066 AST_THREADSTORAGE(custom_buf);
00067 
00068 static const char name[] = "cdr-custom";
00069 
00070 struct cdr_custom_config {
00071    AST_DECLARE_STRING_FIELDS(
00072       AST_STRING_FIELD(filename);
00073       AST_STRING_FIELD(format);
00074       );
00075    ast_mutex_t lock;
00076    AST_RWLIST_ENTRY(cdr_custom_config) list;
00077 };
00078 
00079 static AST_RWLIST_HEAD_STATIC(sinks, cdr_custom_config);
00080 
00081 static void free_config(void)
00082 {
00083    struct cdr_custom_config *sink;
00084    while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
00085       ast_mutex_destroy(&sink->lock);
00086       ast_free(sink);
00087    }
00088 }
00089 
00090 static int load_config(void)
00091 {
00092    struct ast_config *cfg;
00093    struct ast_variable *var;
00094    struct ast_flags config_flags = { 0 };
00095    int res = 0;
00096 
00097    cfg = ast_config_load(CONFIG, config_flags);
00098    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00099       ast_log(LOG_ERROR, "Unable to load " CONFIG ". Not logging custom CSV CDRs.\n");
00100       return -1;
00101    }
00102 
00103    var = ast_variable_browse(cfg, "mappings");
00104    while (var) {
00105       if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
00106          struct cdr_custom_config *sink = ast_calloc_with_stringfields(1, struct cdr_custom_config, 1024);
00107 
00108          if (!sink) {
00109             ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
00110             res = -2;
00111             break;
00112          }
00113 
00114          ast_string_field_build(sink, format, "%s\n", var->value);
00115          ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
00116          ast_mutex_init(&sink->lock);
00117 
00118          AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
00119       } else {
00120          ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno);
00121       }
00122       var = var->next;
00123    }
00124    ast_config_destroy(cfg);
00125 
00126    return res;
00127 }
00128 
00129 static int custom_log(struct ast_cdr *cdr)
00130 {
00131    struct ast_channel *dummy;
00132    struct ast_str *str;
00133    struct cdr_custom_config *config;
00134 
00135    /* Batching saves memory management here.  Otherwise, it's the same as doing an allocation and free each time. */
00136    if (!(str = ast_str_thread_get(&custom_buf, 16))) {
00137       return -1;
00138    }
00139 
00140    dummy = ast_dummy_channel_alloc();
00141    if (!dummy) {
00142       ast_log(LOG_ERROR, "Unable to allocate channel for variable subsitution.\n");
00143       return -1;
00144    }
00145 
00146    /* We need to dup here since the cdr actually belongs to the other channel,
00147       so when we release this channel we don't want the CDR getting cleaned
00148       up prematurely. */
00149    ast_channel_cdr_set(dummy, ast_cdr_dup(cdr));
00150 
00151    AST_RWLIST_RDLOCK(&sinks);
00152 
00153    AST_LIST_TRAVERSE(&sinks, config, list) {
00154       FILE *out;
00155 
00156       ast_str_substitute_variables(&str, 0, dummy, config->format);
00157 
00158       /* Even though we have a lock on the list, we could be being chased by
00159          another thread and this lock ensures that we won't step on anyone's
00160          toes.  Once each CDR backend gets it's own thread, this lock can be
00161          removed. */
00162       ast_mutex_lock(&config->lock);
00163 
00164       /* Because of the absolutely unconditional need for the
00165          highest reliability possible in writing billing records,
00166          we open write and close the log file each time */
00167       if ((out = fopen(config->filename, "a"))) {
00168          fputs(ast_str_buffer(str), out);
00169          fflush(out); /* be particularly anal here */
00170          fclose(out);
00171       } else {
00172          ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
00173       }
00174 
00175       ast_mutex_unlock(&config->lock);
00176    }
00177 
00178    AST_RWLIST_UNLOCK(&sinks);
00179 
00180    ast_channel_unref(dummy);
00181 
00182    return 0;
00183 }
00184 
00185 static int unload_module(void)
00186 {
00187    if (ast_cdr_unregister(name)) {
00188       return -1;
00189    }
00190 
00191    if (AST_RWLIST_WRLOCK(&sinks)) {
00192       ast_cdr_register(name, ast_module_info->description, custom_log);
00193       ast_log(LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
00194       return -1;
00195    }
00196 
00197    free_config();
00198    AST_RWLIST_UNLOCK(&sinks);
00199    return 0;
00200 }
00201 
00202 static enum ast_module_load_result load_module(void)
00203 {
00204    if (AST_RWLIST_WRLOCK(&sinks)) {
00205       ast_log(LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
00206       return AST_MODULE_LOAD_FAILURE;
00207    }
00208 
00209    load_config();
00210    AST_RWLIST_UNLOCK(&sinks);
00211    ast_cdr_register(name, ast_module_info->description, custom_log);
00212    return AST_MODULE_LOAD_SUCCESS;
00213 }
00214 
00215 static int reload(void)
00216 {
00217    if (AST_RWLIST_WRLOCK(&sinks)) {
00218       ast_log(LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
00219       return AST_MODULE_LOAD_FAILURE;
00220    }
00221 
00222    free_config();
00223    load_config();
00224    AST_RWLIST_UNLOCK(&sinks);
00225    return AST_MODULE_LOAD_SUCCESS;
00226 }
00227 
00228 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable Comma Separated Values CDR Backend",
00229       .support_level = AST_MODULE_SUPPORT_CORE,
00230       .load = load_module,
00231       .unload = unload_module,
00232       .reload = reload,
00233       .load_pri = AST_MODPRI_CDR_DRIVER,
00234           );
00235 

Generated on Thu Apr 16 06:27:17 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6