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>
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 #include "asterisk/module.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/cdr.h"
41 #include "asterisk/pbx.h"
45 #include "asterisk/syslog.h"
47 static const char CONFIG[] = "cdr_syslog.conf";
49 AST_THREADSTORAGE(syslog_buf);
51 static const char name[] = "cdr-syslog";
54 AST_DECLARE_STRING_FIELDS(
55 AST_STRING_FIELD(ident);
56 AST_STRING_FIELD(format);
61 AST_LIST_ENTRY(cdr_config) list;
64 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
66 static void free_config(void)
68 struct cdr_config *sink;
69 while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
70 ast_mutex_destroy(&sink->lock);
75 static int syslog_log(struct ast_cdr *cdr)
77 struct ast_channel *dummy;
79 struct cdr_config *sink;
81 /* Batching saves memory management here. Otherwise, it's the same as doing an
82 allocation and free each time. */
83 if (!(str = ast_str_thread_get(&syslog_buf, 16))) {
87 if (!(dummy = ast_dummy_channel_alloc())) {
88 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
92 /* We need to dup here since the cdr actually belongs to the other channel,
93 so when we release this channel we don't want the CDR getting cleaned
95 dummy->cdr = ast_cdr_dup(cdr);
97 AST_RWLIST_RDLOCK(&sinks);
99 AST_LIST_TRAVERSE(&sinks, sink, list) {
101 ast_str_substitute_variables(&str, 0, dummy, sink->format);
103 /* Even though we have a lock on the list, we could be being chased by
104 another thread and this lock ensures that we won't step on anyone's
105 toes. Once each CDR backend gets it's own thread, this lock can be
107 ast_mutex_lock(&sink->lock);
109 openlog(sink->ident, LOG_CONS, sink->facility);
110 syslog(sink->priority, "%s", ast_str_buffer(str));
113 ast_mutex_unlock(&sink->lock);
116 AST_RWLIST_UNLOCK(&sinks);
118 ast_channel_release(dummy);
123 static int load_config(int reload)
125 struct ast_config *cfg;
126 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
127 int default_facility = LOG_LOCAL4;
128 int default_priority = LOG_INFO;
129 const char *catg = NULL, *tmp;
131 cfg = ast_config_load(CONFIG, config_flags);
132 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
133 ast_log(AST_LOG_ERROR,
134 "Unable to load %s. Not logging custom CSV CDRs to syslog.\n", CONFIG);
136 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
144 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
145 int facility = ast_syslog_facility(tmp);
147 ast_log(AST_LOG_WARNING,
148 "Invalid facility '%s' specified, defaulting to '%s'\n",
149 tmp, ast_syslog_facility_name(default_facility));
151 default_facility = facility;
155 if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
156 int priority = ast_syslog_priority(tmp);
158 ast_log(AST_LOG_WARNING,
159 "Invalid priority '%s' specified, defaulting to '%s'\n",
160 tmp, ast_syslog_priority_name(default_priority));
162 default_priority = priority;
166 while ((catg = ast_category_browse(cfg, catg))) {
167 struct cdr_config *sink;
169 if (!strcasecmp(catg, "general")) {
173 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "template"))) {
174 ast_log(AST_LOG_WARNING,
175 "No 'template' parameter found for '%s'. Skipping.\n", catg);
179 sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
182 ast_log(AST_LOG_ERROR,
183 "Unable to allocate memory for configuration settings.\n");
188 ast_mutex_init(&sink->lock);
189 ast_string_field_set(sink, ident, catg);
190 ast_string_field_set(sink, format, tmp);
192 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
193 sink->facility = default_facility;
195 int facility = ast_syslog_facility(tmp);
197 ast_log(AST_LOG_WARNING,
198 "Invalid facility '%s' specified for '%s,' defaulting to '%s'\n",
199 tmp, catg, ast_syslog_facility_name(default_facility));
201 sink->facility = facility;
205 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
206 sink->priority = default_priority;
208 int priority = ast_syslog_priority(tmp);
210 ast_log(AST_LOG_WARNING,
211 "Invalid priority '%s' specified for '%s,' defaulting to '%s'\n",
212 tmp, catg, ast_syslog_priority_name(default_priority));
214 sink->priority = priority;
218 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
221 ast_config_destroy(cfg);
223 return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
226 static int unload_module(void)
228 ast_cdr_unregister(name);
230 if (AST_RWLIST_WRLOCK(&sinks)) {
231 ast_cdr_register(name, ast_module_info->description, syslog_log);
232 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Unload failed.\n");
237 AST_RWLIST_UNLOCK(&sinks);
241 static enum ast_module_load_result load_module(void)
245 if (AST_RWLIST_WRLOCK(&sinks)) {
246 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
247 return AST_MODULE_LOAD_DECLINE;
250 res = load_config(0);
251 AST_RWLIST_UNLOCK(&sinks);
253 return AST_MODULE_LOAD_DECLINE;
255 ast_cdr_register(name, ast_module_info->description, syslog_log);
256 return AST_MODULE_LOAD_SUCCESS;
259 static int reload(void)
262 if (AST_RWLIST_WRLOCK(&sinks)) {
263 ast_log(AST_LOG_ERROR, "Unable to lock sink list. Load failed.\n");
264 return AST_MODULE_LOAD_DECLINE;
267 if ((res = load_config(1))) {
271 AST_RWLIST_UNLOCK(&sinks);
273 return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
276 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
278 .unload = unload_module,
280 .load_pri = AST_MODPRI_CDR_DRIVER,