cdr_syslog.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2009, malleable, LLC.
00005  *
00006  * Sean Bright <sean@malleable.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  * \brief syslog CDR logger
00022  *
00023  * \author Sean Bright <sean@malleable.com>
00024  *
00025  * See also
00026  * \arg \ref Config_cdr
00027  * \ingroup cdr_drivers
00028  */
00029 
00030 /*! \li \ref cdr_syslog.c uses the configuration file \ref cdr_syslog.conf
00031  * \addtogroup configuration_file Configuration Files
00032  */
00033 
00034 /*!
00035  * \page cdr_syslog.conf cdr_syslog.conf
00036  * \verbinclude cdr_syslog.conf.sample
00037  */
00038 
00039 /*** MODULEINFO
00040    <depend>syslog</depend>
00041    <support_level>core</support_level>
00042 ***/
00043 
00044 #include "asterisk.h"
00045 
00046 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 419592 $")
00047 
00048 #include "asterisk/module.h"
00049 #include "asterisk/lock.h"
00050 #include "asterisk/cdr.h"
00051 #include "asterisk/pbx.h"
00052 
00053 #include <syslog.h>
00054 
00055 #include "asterisk/syslog.h"
00056 
00057 static const char CONFIG[] = "cdr_syslog.conf";
00058 
00059 AST_THREADSTORAGE(syslog_buf);
00060 
00061 static const char name[] = "cdr-syslog";
00062 
00063 struct cdr_syslog_config {
00064    AST_DECLARE_STRING_FIELDS(
00065       AST_STRING_FIELD(ident);
00066       AST_STRING_FIELD(format);
00067    );
00068    int facility;
00069    int priority;
00070    ast_mutex_t lock;
00071    AST_LIST_ENTRY(cdr_syslog_config) list;
00072 };
00073 
00074 static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
00075 
00076 static void free_config(void)
00077 {
00078    struct cdr_syslog_config *sink;
00079    while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
00080       ast_mutex_destroy(&sink->lock);
00081       ast_free(sink);
00082    }
00083 }
00084 
00085 static int syslog_log(struct ast_cdr *cdr)
00086 {
00087    struct ast_channel *dummy;
00088    struct ast_str *str;
00089    struct cdr_syslog_config *sink;
00090 
00091    /* Batching saves memory management here.  Otherwise, it's the same as doing an
00092       allocation and free each time. */
00093    if (!(str = ast_str_thread_get(&syslog_buf, 16))) {
00094       return -1;
00095    }
00096 
00097    if (!(dummy = ast_dummy_channel_alloc())) {
00098       ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
00099       return -1;
00100    }
00101 
00102    /* We need to dup here since the cdr actually belongs to the other channel,
00103       so when we release this channel we don't want the CDR getting cleaned
00104       up prematurely. */
00105    ast_channel_cdr_set(dummy, ast_cdr_dup(cdr));
00106 
00107    AST_RWLIST_RDLOCK(&sinks);
00108 
00109    AST_LIST_TRAVERSE(&sinks, sink, list) {
00110 
00111       ast_str_substitute_variables(&str, 0, dummy, sink->format);
00112 
00113       /* Even though we have a lock on the list, we could be being chased by
00114          another thread and this lock ensures that we won't step on anyone's
00115          toes.  Once each CDR backend gets it's own thread, this lock can be
00116          removed. */
00117       ast_mutex_lock(&sink->lock);
00118 
00119       openlog(sink->ident, LOG_CONS, sink->facility);
00120       syslog(sink->priority, "%s", ast_str_buffer(str));
00121       closelog();
00122 
00123       ast_mutex_unlock(&sink->lock);
00124    }
00125 
00126    AST_RWLIST_UNLOCK(&sinks);
00127 
00128    ast_channel_unref(dummy);
00129 
00130    return 0;
00131 }
00132 
00133 static int load_config(int reload)
00134 {
00135    struct ast_config *cfg;
00136    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00137    int default_facility = LOG_LOCAL4;
00138    int default_priority = LOG_INFO;
00139    const char *catg = NULL, *tmp;
00140 
00141    cfg = ast_config_load(CONFIG, config_flags);
00142    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
00143       ast_log(AST_LOG_ERROR,
00144          "Unable to load %s. Not logging custom CSV CDRs to syslog.\n", CONFIG);
00145       return -1;
00146    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
00147       return 0;
00148    }
00149 
00150    if (reload) {
00151       free_config();
00152    }
00153 
00154    if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
00155       int facility = ast_syslog_facility(tmp);
00156       if (facility < 0) {
00157          ast_log(AST_LOG_WARNING,
00158             "Invalid facility '%s' specified, defaulting to '%s'\n",
00159             tmp, ast_syslog_facility_name(default_facility));
00160       } else {
00161          default_facility = facility;
00162       }
00163    }
00164 
00165    if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
00166       int priority = ast_syslog_priority(tmp);
00167       if (priority < 0) {
00168          ast_log(AST_LOG_WARNING,
00169             "Invalid priority '%s' specified, defaulting to '%s'\n",
00170             tmp, ast_syslog_priority_name(default_priority));
00171       } else {
00172          default_priority = priority;
00173       }
00174    }
00175 
00176    while ((catg = ast_category_browse(cfg, catg))) {
00177       struct cdr_syslog_config *sink;
00178 
00179       if (!strcasecmp(catg, "general")) {
00180          continue;
00181       }
00182 
00183       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "template"))) {
00184          ast_log(AST_LOG_WARNING,
00185             "No 'template' parameter found for '%s'.  Skipping.\n", catg);
00186          continue;
00187       }
00188 
00189       sink = ast_calloc_with_stringfields(1, struct cdr_syslog_config, 1024);
00190 
00191       if (!sink) {
00192          ast_log(AST_LOG_ERROR,
00193             "Unable to allocate memory for configuration settings.\n");
00194          free_config();
00195          break;
00196       }
00197 
00198       ast_mutex_init(&sink->lock);
00199       ast_string_field_set(sink, ident, catg);
00200       ast_string_field_set(sink, format, tmp);
00201 
00202       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
00203          sink->facility = default_facility;
00204       } else {
00205          int facility = ast_syslog_facility(tmp);
00206          if (facility < 0) {
00207             ast_log(AST_LOG_WARNING,
00208                "Invalid facility '%s' specified for '%s,' defaulting to '%s'\n",
00209                tmp, catg, ast_syslog_facility_name(default_facility));
00210          } else {
00211             sink->facility = facility;
00212          }
00213       }
00214 
00215       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
00216          sink->priority = default_priority;
00217       } else {
00218          int priority = ast_syslog_priority(tmp);
00219          if (priority < 0) {
00220             ast_log(AST_LOG_WARNING,
00221                "Invalid priority '%s' specified for '%s,' defaulting to '%s'\n",
00222                tmp, catg, ast_syslog_priority_name(default_priority));
00223          } else {
00224             sink->priority = priority;
00225          }
00226       }
00227 
00228       AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
00229    }
00230 
00231    ast_config_destroy(cfg);
00232 
00233    return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
00234 }
00235 
00236 static int unload_module(void)
00237 {
00238    if (ast_cdr_unregister(name)) {
00239       return -1;
00240    }
00241 
00242    if (AST_RWLIST_WRLOCK(&sinks)) {
00243       ast_cdr_register(name, ast_module_info->description, syslog_log);
00244       ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
00245       return -1;
00246    }
00247 
00248    free_config();
00249    AST_RWLIST_UNLOCK(&sinks);
00250    return 0;
00251 }
00252 
00253 static enum ast_module_load_result load_module(void)
00254 {
00255    int res;
00256 
00257    if (AST_RWLIST_WRLOCK(&sinks)) {
00258       ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
00259       return AST_MODULE_LOAD_DECLINE;
00260    }
00261 
00262    res = load_config(0);
00263    AST_RWLIST_UNLOCK(&sinks);
00264    if (res) {
00265       return AST_MODULE_LOAD_DECLINE;
00266    }
00267    ast_cdr_register(name, ast_module_info->description, syslog_log);
00268    return AST_MODULE_LOAD_SUCCESS;
00269 }
00270 
00271 static int reload(void)
00272 {
00273    int res;
00274    if (AST_RWLIST_WRLOCK(&sinks)) {
00275       ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
00276       return AST_MODULE_LOAD_DECLINE;
00277    }
00278 
00279    if ((res = load_config(1))) {
00280       free_config();
00281    }
00282 
00283    AST_RWLIST_UNLOCK(&sinks);
00284 
00285    return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
00286 }
00287 
00288 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
00289    .support_level = AST_MODULE_SUPPORT_CORE,
00290    .load = load_module,
00291    .unload = unload_module,
00292    .reload = reload,
00293    .load_pri = AST_MODPRI_CDR_DRIVER,
00294 );

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