2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2009, malleable, LLC.
6 * Sean Bright <sean@malleable.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief syslog CDR logger
23 * \author Sean Bright <sean@malleable.com>
26 * \arg \ref Config_cdr
27 * \ingroup cdr_drivers
30 /*! \li \ref cdr_syslog.c uses the configuration file \ref cdr_syslog.conf
31 * \addtogroup configuration_file Configuration Files
35 * \page cdr_syslog.conf cdr_syslog.conf
36 * \verbinclude cdr_syslog.conf.sample
40 <depend>syslog</depend>
41 <support_level>core</support_level>
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/module.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/cdr.h"
51 #include "asterisk/pbx.h"
55 #include "asterisk/syslog.h"
57 static const char CONFIG[] = "cdr_syslog.conf";
59 AST_THREADSTORAGE(syslog_buf);
61 static const char name[] = "cdr-syslog";
63 struct cdr_syslog_config {
64 AST_DECLARE_STRING_FIELDS(
65 AST_STRING_FIELD(ident);
66 AST_STRING_FIELD(format);
71 AST_LIST_ENTRY(cdr_syslog_config) list;
74 static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
76 static void free_config(void)
78 struct cdr_syslog_config *sink;
79 while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
80 ast_mutex_destroy(&sink->lock);
85 static int syslog_log(struct ast_cdr *cdr)
87 struct ast_channel *dummy;
89 struct cdr_syslog_config *sink;
91 /* Batching saves memory management here. Otherwise, it's the same as doing an
92 allocation and free each time. */
93 if (!(str = ast_str_thread_get(&syslog_buf, 16))) {
97 if (!(dummy = ast_dummy_channel_alloc())) {
98 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
102 /* We need to dup here since the cdr actually belongs to the other channel,
103 so when we release this channel we don't want the CDR getting cleaned
105 ast_channel_cdr_set(dummy, ast_cdr_dup(cdr));
107 AST_RWLIST_RDLOCK(&sinks);
109 AST_LIST_TRAVERSE(&sinks, sink, list) {
111 ast_str_substitute_variables(&str, 0, dummy, sink->format);
113 /* Even though we have a lock on the list, we could be being chased by
114 another thread and this lock ensures that we won't step on anyone's
115 toes. Once each CDR backend gets it's own thread, this lock can be
117 ast_mutex_lock(&sink->lock);
119 openlog(sink->ident, LOG_CONS, sink->facility);
120 syslog(sink->priority, "%s", ast_str_buffer(str));
123 ast_mutex_unlock(&sink->lock);
126 AST_RWLIST_UNLOCK(&sinks);
128 ast_channel_unref(dummy);
133 static int load_config(int reload)
135 struct ast_config *cfg;
136 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
137 int default_facility = LOG_LOCAL4;
138 int default_priority = LOG_INFO;
139 const char *catg = NULL, *tmp;
141 cfg = ast_config_load(CONFIG, config_flags);
142 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
143 ast_log(AST_LOG_ERROR,
144 "Unable to load %s. Not logging custom CSV CDRs to syslog.\n", CONFIG);
146 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
154 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
155 int facility = ast_syslog_facility(tmp);
157 ast_log(AST_LOG_WARNING,
158 "Invalid facility '%s' specified, defaulting to '%s'\n",
159 tmp, ast_syslog_facility_name(default_facility));
161 default_facility = facility;
165 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
166 int priority = ast_syslog_priority(tmp);
168 ast_log(AST_LOG_WARNING,
169 "Invalid priority '%s' specified, defaulting to '%s'\n",
170 tmp, ast_syslog_priority_name(default_priority));
172 default_priority = priority;
176 while ((catg = ast_category_browse(cfg, catg))) {
177 struct cdr_syslog_config *sink;
179 if (!strcasecmp(catg, "general")) {
183 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "template"))) {
184 ast_log(AST_LOG_WARNING,
185 "No 'template' parameter found for '%s'. Skipping.\n", catg);
189 sink = ast_calloc_with_stringfields(1, struct cdr_syslog_config, 1024);
192 ast_log(AST_LOG_ERROR,
193 "Unable to allocate memory for configuration settings.\n");
198 ast_mutex_init(&sink->lock);
199 ast_string_field_set(sink, ident, catg);
200 ast_string_field_set(sink, format, tmp);
202 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
203 sink->facility = default_facility;
205 int facility = ast_syslog_facility(tmp);
207 ast_log(AST_LOG_WARNING,
208 "Invalid facility '%s' specified for '%s,' defaulting to '%s'\n",
209 tmp, catg, ast_syslog_facility_name(default_facility));
211 sink->facility = facility;
215 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
216 sink->priority = default_priority;
218 int priority = ast_syslog_priority(tmp);
220 ast_log(AST_LOG_WARNING,
221 "Invalid priority '%s' specified for '%s,' defaulting to '%s'\n",
222 tmp, catg, ast_syslog_priority_name(default_priority));
224 sink->priority = priority;
228 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
231 ast_config_destroy(cfg);
233 return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
236 static int unload_module(void)
238 if (ast_cdr_unregister(name)) {
242 if (AST_RWLIST_WRLOCK(&sinks)) {
243 ast_cdr_register(name, ast_module_info->description, syslog_log);
244 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
249 AST_RWLIST_UNLOCK(&sinks);
253 static enum ast_module_load_result load_module(void)
257 if (AST_RWLIST_WRLOCK(&sinks)) {
258 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
259 return AST_MODULE_LOAD_DECLINE;
262 res = load_config(0);
263 AST_RWLIST_UNLOCK(&sinks);
265 return AST_MODULE_LOAD_DECLINE;
267 ast_cdr_register(name, ast_module_info->description, syslog_log);
268 return AST_MODULE_LOAD_SUCCESS;
271 static int reload(void)
274 if (AST_RWLIST_WRLOCK(&sinks)) {
275 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
276 return AST_MODULE_LOAD_DECLINE;
279 if ((res = load_config(1))) {
283 AST_RWLIST_UNLOCK(&sinks);
285 return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
288 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
290 .unload = unload_module,
292 .load_pri = AST_MODPRI_CDR_DRIVER,