cel_custom.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009, Digium, Inc.
00005  *
00006  * Steve Murphy <murf@digium.com>
00007  * much borrowed from cdr code (cdr_custom.c), author Mark Spencer
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Custom Comma Separated Value CEL records.
00023  *
00024  * \author Steve Murphy <murf@digium.com>
00025  *
00026  * \arg See also \ref AstCEL
00027  *
00028  * Logs in LOG_DIR/cel_custom
00029  * \ingroup cel_drivers
00030  */
00031 
00032 /*** MODULEINFO
00033    <support_level>core</support_level>
00034  ***/
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
00039 
00040 #include "asterisk/paths.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/cel.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/config.h"
00045 #include "asterisk/pbx.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/lock.h"
00048 #include "asterisk/threadstorage.h"
00049 #include "asterisk/strings.h"
00050 
00051 #define CUSTOM_LOG_DIR "/cel_custom"
00052 #define CONFIG         "cel_custom.conf"
00053 
00054 AST_THREADSTORAGE(custom_buf);
00055 
00056 static const char name[] = "cel-custom";
00057 
00058 struct cel_config {
00059    AST_DECLARE_STRING_FIELDS(
00060       AST_STRING_FIELD(filename);
00061       AST_STRING_FIELD(format);
00062    );
00063    ast_mutex_t lock;
00064    AST_RWLIST_ENTRY(cel_config) list;
00065 };
00066 
00067 #define CUSTOM_BACKEND_NAME "CEL Custom CSV Logging"
00068 
00069 static AST_RWLIST_HEAD_STATIC(sinks, cel_config);
00070 
00071 static void free_config(void)
00072 {
00073    struct cel_config *sink;
00074    while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
00075       ast_mutex_destroy(&sink->lock);
00076       ast_free(sink);
00077    }
00078 }
00079 
00080 static int load_config(void)
00081 {
00082    struct ast_config *cfg;
00083    struct ast_variable *var;
00084    struct ast_flags config_flags = { 0 };
00085    int mappings = 0;
00086    int res = 0;
00087 
00088    cfg = ast_config_load(CONFIG, config_flags);
00089    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00090       ast_log(LOG_ERROR, "Unable to load " CONFIG ". Not logging CEL to custom CSVs.\n");
00091       return -1;
00092    }
00093 
00094    if (!(var = ast_variable_browse(cfg, "mappings"))) {
00095       ast_log(LOG_NOTICE, "No mappings found in " CONFIG ". Not logging CEL to custom CSVs.\n");
00096    }
00097 
00098    while (var) {
00099       if (!ast_strlen_zero(var->name) && !ast_strlen_zero(var->value)) {
00100          struct cel_config *sink = ast_calloc_with_stringfields(1, struct cel_config, 1024);
00101 
00102          if (!sink) {
00103             ast_log(LOG_ERROR, "Unable to allocate memory for configuration settings.\n");
00104             res = -2;
00105             break;
00106          }
00107 
00108          ast_string_field_build(sink, format, "%s\n", var->value);
00109          ast_string_field_build(sink, filename, "%s/%s/%s", ast_config_AST_LOG_DIR, name, var->name);
00110          ast_mutex_init(&sink->lock);
00111 
00112          ast_verb(3, "Added CEL CSV mapping for '%s'.\n", sink->filename);
00113          mappings += 1;
00114          AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
00115       } else {
00116          ast_log(LOG_NOTICE, "Mapping must have both a filename and a format at line %d\n", var->lineno);
00117       }
00118       var = var->next;
00119    }
00120    ast_config_destroy(cfg);
00121 
00122    ast_verb(1, "Added CEL CSV mapping for %d files.\n", mappings);
00123 
00124    return res;
00125 }
00126 
00127 static void custom_log(struct ast_event *event)
00128 {
00129    struct ast_channel *dummy;
00130    struct ast_str *str;
00131    struct cel_config *config;
00132 
00133    /* Batching saves memory management here.  Otherwise, it's the same as doing an allocation and free each time. */
00134    if (!(str = ast_str_thread_get(&custom_buf, 16))) {
00135       return;
00136    }
00137 
00138    dummy = ast_cel_fabricate_channel_from_event(event);
00139    if (!dummy) {
00140       ast_log(LOG_ERROR, "Unable to fabricate channel from CEL event.\n");
00141       return;
00142    }
00143 
00144    AST_RWLIST_RDLOCK(&sinks);
00145 
00146    AST_LIST_TRAVERSE(&sinks, config, list) {
00147       FILE *out;
00148 
00149       ast_str_substitute_variables(&str, 0, dummy, config->format);
00150 
00151       /* Even though we have a lock on the list, we could be being chased by
00152          another thread and this lock ensures that we won't step on anyone's
00153          toes.  Once each CEL backend gets it's own thread, this lock can be
00154          removed. */
00155       ast_mutex_lock(&config->lock);
00156 
00157       /* Because of the absolutely unconditional need for the
00158          highest reliability possible in writing billing records,
00159          we open write and close the log file each time */
00160       if ((out = fopen(config->filename, "a"))) {
00161          fputs(ast_str_buffer(str), out);
00162          fflush(out); /* be particularly anal here */
00163          fclose(out);
00164       } else {
00165          ast_log(LOG_ERROR, "Unable to re-open master file %s : %s\n", config->filename, strerror(errno));
00166       }
00167 
00168       ast_mutex_unlock(&config->lock);
00169    }
00170 
00171    AST_RWLIST_UNLOCK(&sinks);
00172 
00173    ast_channel_unref(dummy);
00174 }
00175 
00176 static int unload_module(void)
00177 {
00178 
00179    if (AST_RWLIST_WRLOCK(&sinks)) {
00180       ast_log(LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
00181       return -1;
00182    }
00183 
00184    free_config();
00185    AST_RWLIST_UNLOCK(&sinks);
00186    ast_cel_backend_unregister(CUSTOM_BACKEND_NAME);
00187    return 0;
00188 }
00189 
00190 static enum ast_module_load_result load_module(void)
00191 {
00192    if (AST_RWLIST_WRLOCK(&sinks)) {
00193       ast_log(LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
00194       return AST_MODULE_LOAD_FAILURE;
00195    }
00196 
00197    load_config();
00198    AST_RWLIST_UNLOCK(&sinks);
00199 
00200    if (ast_cel_backend_register(CUSTOM_BACKEND_NAME, custom_log)) {
00201       return AST_MODULE_LOAD_FAILURE;
00202    }
00203    return AST_MODULE_LOAD_SUCCESS;
00204 }
00205 
00206 static int reload(void)
00207 {
00208    if (AST_RWLIST_WRLOCK(&sinks)) {
00209       ast_log(LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
00210       return AST_MODULE_LOAD_FAILURE;
00211    }
00212 
00213    free_config();
00214    load_config();
00215    AST_RWLIST_UNLOCK(&sinks);
00216    return AST_MODULE_LOAD_SUCCESS;
00217 }
00218 
00219 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable Comma Separated Values CEL Backend",
00220    .support_level = AST_MODULE_SUPPORT_CORE,
00221    .load = load_module,
00222    .unload = unload_module,
00223    .reload = reload,
00224    .load_pri = AST_MODPRI_CDR_DRIVER,
00225    );
00226 

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