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.
19 /*! \file cdr_syslog.c
21 * \brief syslog CDR logger
22 * \author Sean Bright <sean@malleable.com>
25 * \arg \ref Config_cdr
26 * \ingroup cdr_drivers
30 <depend>syslog</depend>
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37 #include "asterisk/module.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/cdr.h"
40 #include "asterisk/pbx.h"
44 #include "asterisk/syslog.h"
46 static const char CONFIG[] = "cdr_syslog.conf";
48 AST_THREADSTORAGE(syslog_buf);
50 static const char name[] = "cdr-syslog";
53 AST_DECLARE_STRING_FIELDS(
54 AST_STRING_FIELD(ident);
55 AST_STRING_FIELD(format);
60 AST_LIST_ENTRY(cdr_config) list;
63 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
65 static void free_config(void)
67 struct cdr_config *sink;
68 while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
69 ast_mutex_destroy(&sink->lock);
74 static int syslog_log(struct ast_cdr *cdr)
76 struct ast_channel *dummy;
78 struct cdr_config *sink;
80 /* Batching saves memory management here. Otherwise, it's the same as doing an
81 allocation and free each time. */
82 if (!(str = ast_str_thread_get(&syslog_buf, 16))) {
86 if (!(dummy = ast_dummy_channel_alloc())) {
87 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
91 /* We need to dup here since the cdr actually belongs to the other channel,
92 so when we release this channel we don't want the CDR getting cleaned
94 dummy->cdr = ast_cdr_dup(cdr);
96 AST_RWLIST_RDLOCK(&sinks);
98 AST_LIST_TRAVERSE(&sinks, sink, list) {
100 ast_str_substitute_variables(&str, 0, dummy, sink->format);
102 /* Even though we have a lock on the list, we could be being chased by
103 another thread and this lock ensures that we won't step on anyone's
104 toes. Once each CDR backend gets it's own thread, this lock can be
106 ast_mutex_lock(&sink->lock);
108 openlog(sink->ident, LOG_CONS, sink->facility);
109 syslog(sink->priority, "%s", ast_str_buffer(str));
112 ast_mutex_unlock(&sink->lock);
115 AST_RWLIST_UNLOCK(&sinks);
117 ast_channel_release(dummy);
122 static int load_config(int reload)
124 struct ast_config *cfg;
125 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
126 int default_facility = LOG_LOCAL4;
127 int default_priority = LOG_INFO;
128 const char *catg = NULL, *tmp;
130 cfg = ast_config_load(CONFIG, config_flags);
131 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
132 ast_log(AST_LOG_ERROR,
133 "Unable to load %s. Not logging custom CSV CDRs to syslog.\n", CONFIG);
135 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
143 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
144 int facility = ast_syslog_facility(tmp);
146 ast_log(AST_LOG_WARNING,
147 "Invalid facility '%s' specified, defaulting to '%s'\n",
148 tmp, ast_syslog_facility_name(default_facility));
150 default_facility = facility;
154 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
155 int priority = ast_syslog_priority(tmp);
157 ast_log(AST_LOG_WARNING,
158 "Invalid priority '%s' specified, defaulting to '%s'\n",
159 tmp, ast_syslog_priority_name(default_priority));
161 default_priority = priority;
165 while ((catg = ast_category_browse(cfg, catg))) {
166 struct cdr_config *sink;
168 if (!strcasecmp(catg, "general")) {
172 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "template"))) {
173 ast_log(AST_LOG_WARNING,
174 "No 'template' parameter found for '%s'. Skipping.\n", catg);
178 sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
181 ast_log(AST_LOG_ERROR,
182 "Unable to allocate memory for configuration settings.\n");
187 ast_mutex_init(&sink->lock);
188 ast_string_field_set(sink, ident, catg);
189 ast_string_field_set(sink, format, tmp);
191 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
192 sink->facility = default_facility;
194 int facility = ast_syslog_facility(tmp);
196 ast_log(AST_LOG_WARNING,
197 "Invalid facility '%s' specified for '%s,' defaulting to '%s'\n",
198 tmp, catg, ast_syslog_facility_name(default_facility));
200 sink->facility = facility;
204 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
205 sink->priority = default_priority;
207 int priority = ast_syslog_priority(tmp);
209 ast_log(AST_LOG_WARNING,
210 "Invalid priority '%s' specified for '%s,' defaulting to '%s'\n",
211 tmp, catg, ast_syslog_priority_name(default_priority));
213 sink->priority = priority;
217 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
220 ast_config_destroy(cfg);
222 return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
225 static int unload_module(void)
227 ast_cdr_unregister(name);
229 if (AST_RWLIST_WRLOCK(&sinks)) {
230 ast_cdr_register(name, ast_module_info->description, syslog_log);
231 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
236 AST_RWLIST_UNLOCK(&sinks);
240 static enum ast_module_load_result load_module(void)
244 if (AST_RWLIST_WRLOCK(&sinks)) {
245 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
246 return AST_MODULE_LOAD_DECLINE;
249 res = load_config(0);
250 AST_RWLIST_UNLOCK(&sinks);
252 return AST_MODULE_LOAD_DECLINE;
254 ast_cdr_register(name, ast_module_info->description, syslog_log);
255 return AST_MODULE_LOAD_SUCCESS;
258 static int reload(void)
261 if (AST_RWLIST_WRLOCK(&sinks)) {
262 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
263 return AST_MODULE_LOAD_DECLINE;
267 res = load_config(1);
268 AST_RWLIST_UNLOCK(&sinks);
270 return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
273 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Customizable syslog CDR Backend",
275 .unload = unload_module,