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
31 <depend>syslog</depend>
32 <support_level>core</support_level>
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include "asterisk/module.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/cdr.h"
42 #include "asterisk/pbx.h"
46 #include "asterisk/syslog.h"
48 static const char CONFIG[] = "cdr_syslog.conf";
50 AST_THREADSTORAGE(syslog_buf);
52 static const char name[] = "cdr-syslog";
55 AST_DECLARE_STRING_FIELDS(
56 AST_STRING_FIELD(ident);
57 AST_STRING_FIELD(format);
62 AST_LIST_ENTRY(cdr_config) list;
65 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
67 static void free_config(void)
69 struct cdr_config *sink;
70 while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
71 ast_mutex_destroy(&sink->lock);
76 static int syslog_log(struct ast_cdr *cdr)
78 struct ast_channel *dummy;
80 struct cdr_config *sink;
82 /* Batching saves memory management here. Otherwise, it's the same as doing an
83 allocation and free each time. */
84 if (!(str = ast_str_thread_get(&syslog_buf, 16))) {
88 if (!(dummy = ast_dummy_channel_alloc())) {
89 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
93 /* We need to dup here since the cdr actually belongs to the other channel,
94 so when we release this channel we don't want the CDR getting cleaned
96 dummy->cdr = ast_cdr_dup(cdr);
98 AST_RWLIST_RDLOCK(&sinks);
100 AST_LIST_TRAVERSE(&sinks, sink, list) {
102 ast_str_substitute_variables(&str, 0, dummy, sink->format);
104 /* Even though we have a lock on the list, we could be being chased by
105 another thread and this lock ensures that we won't step on anyone's
106 toes. Once each CDR backend gets it's own thread, this lock can be
108 ast_mutex_lock(&sink->lock);
110 openlog(sink->ident, LOG_CONS, sink->facility);
111 syslog(sink->priority, "%s", ast_str_buffer(str));
114 ast_mutex_unlock(&sink->lock);
117 AST_RWLIST_UNLOCK(&sinks);
119 ast_channel_unref(dummy);
124 static int load_config(int reload)
126 struct ast_config *cfg;
127 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
128 int default_facility = LOG_LOCAL4;
129 int default_priority = LOG_INFO;
130 const char *catg = NULL, *tmp;
132 cfg = ast_config_load(CONFIG, config_flags);
133 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
134 ast_log(AST_LOG_ERROR,
135 "Unable to load %s. Not logging custom CSV CDRs to syslog.\n", CONFIG);
137 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
145 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
146 int facility = ast_syslog_facility(tmp);
148 ast_log(AST_LOG_WARNING,
149 "Invalid facility '%s' specified, defaulting to '%s'\n",
150 tmp, ast_syslog_facility_name(default_facility));
152 default_facility = facility;
156 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
157 int priority = ast_syslog_priority(tmp);
159 ast_log(AST_LOG_WARNING,
160 "Invalid priority '%s' specified, defaulting to '%s'\n",
161 tmp, ast_syslog_priority_name(default_priority));
163 default_priority = priority;
167 while ((catg = ast_category_browse(cfg, catg))) {
168 struct cdr_config *sink;
170 if (!strcasecmp(catg, "general")) {
174 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "template"))) {
175 ast_log(AST_LOG_WARNING,
176 "No 'template' parameter found for '%s'. Skipping.\n", catg);
180 sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
183 ast_log(AST_LOG_ERROR,
184 "Unable to allocate memory for configuration settings.\n");
189 ast_mutex_init(&sink->lock);
190 ast_string_field_set(sink, ident, catg);
191 ast_string_field_set(sink, format, tmp);
193 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
194 sink->facility = default_facility;
196 int facility = ast_syslog_facility(tmp);
198 ast_log(AST_LOG_WARNING,
199 "Invalid facility '%s' specified for '%s,' defaulting to '%s'\n",
200 tmp, catg, ast_syslog_facility_name(default_facility));
202 sink->facility = facility;
206 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
207 sink->priority = default_priority;
209 int priority = ast_syslog_priority(tmp);
211 ast_log(AST_LOG_WARNING,
212 "Invalid priority '%s' specified for '%s,' defaulting to '%s'\n",
213 tmp, catg, ast_syslog_priority_name(default_priority));
215 sink->priority = priority;
219 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
222 ast_config_destroy(cfg);
224 return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
227 static int unload_module(void)
229 ast_cdr_unregister(name);
231 if (AST_RWLIST_WRLOCK(&sinks)) {
232 ast_cdr_register(name, ast_module_info->description, syslog_log);
233 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
238 AST_RWLIST_UNLOCK(&sinks);
242 static enum ast_module_load_result load_module(void)
246 if (AST_RWLIST_WRLOCK(&sinks)) {
247 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
248 return AST_MODULE_LOAD_DECLINE;
251 res = load_config(0);
252 AST_RWLIST_UNLOCK(&sinks);
254 return AST_MODULE_LOAD_DECLINE;
256 ast_cdr_register(name, ast_module_info->description, syslog_log);
257 return AST_MODULE_LOAD_SUCCESS;
260 static int reload(void)
263 if (AST_RWLIST_WRLOCK(&sinks)) {
264 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
265 return AST_MODULE_LOAD_DECLINE;
268 if ((res = load_config(1))) {
272 AST_RWLIST_UNLOCK(&sinks);
274 return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
277 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
279 .unload = unload_module,
281 .load_pri = AST_MODPRI_CDR_DRIVER,