Doxygen Updates - Title update
[asterisk/asterisk.git] / cdr / cdr_syslog.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, malleable, LLC.
5  *
6  * Sean Bright <sean@malleable.com>
7  *
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.
13  *
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.
17  */
18
19 /*!
20  * \file
21  * \brief syslog CDR logger
22  *
23  * \author Sean Bright <sean@malleable.com>
24  *
25  * See also
26  * \arg \ref Config_cdr
27  * \ingroup cdr_drivers
28  */
29
30 /*! \li \ref cdr_syslog.c uses the configuration file \ref cdr_syslog.conf
31  * \addtogroup configuration_file Configuration Files
32  */
33
34 /*!
35  * \page cdr_syslog.conf cdr_syslog.conf
36  * \verbinclude cdr_syslog.conf.sample
37  */
38
39 /*** MODULEINFO
40         <depend>syslog</depend>
41         <support_level>core</support_level>
42 ***/
43
44 #include "asterisk.h"
45
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47
48 #include "asterisk/module.h"
49 #include "asterisk/lock.h"
50 #include "asterisk/cdr.h"
51 #include "asterisk/pbx.h"
52
53 #include <syslog.h>
54
55 #include "asterisk/syslog.h"
56
57 static const char CONFIG[] = "cdr_syslog.conf";
58
59 AST_THREADSTORAGE(syslog_buf);
60
61 static const char name[] = "cdr-syslog";
62
63 struct cdr_config {
64         AST_DECLARE_STRING_FIELDS(
65                 AST_STRING_FIELD(ident);
66                 AST_STRING_FIELD(format);
67         );
68         int facility;
69         int priority;
70         ast_mutex_t lock;
71         AST_LIST_ENTRY(cdr_config) list;
72 };
73
74 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
75
76 static void free_config(void)
77 {
78         struct cdr_config *sink;
79         while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
80                 ast_mutex_destroy(&sink->lock);
81                 ast_free(sink);
82         }
83 }
84
85 static int syslog_log(struct ast_cdr *cdr)
86 {
87         struct ast_channel *dummy;
88         struct ast_str *str;
89         struct cdr_config *sink;
90
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))) {
94                 return -1;
95         }
96
97         if (!(dummy = ast_dummy_channel_alloc())) {
98                 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
99                 return -1;
100         }
101
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
104            up prematurely. */
105         ast_channel_cdr_set(dummy, ast_cdr_dup(cdr));
106
107         AST_RWLIST_RDLOCK(&sinks);
108
109         AST_LIST_TRAVERSE(&sinks, sink, list) {
110
111                 ast_str_substitute_variables(&str, 0, dummy, sink->format);
112
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
116                    removed. */
117                 ast_mutex_lock(&sink->lock);
118
119                 openlog(sink->ident, LOG_CONS, sink->facility);
120                 syslog(sink->priority, "%s", ast_str_buffer(str));
121                 closelog();
122
123                 ast_mutex_unlock(&sink->lock);
124         }
125
126         AST_RWLIST_UNLOCK(&sinks);
127
128         ast_channel_unref(dummy);
129
130         return 0;
131 }
132
133 static int load_config(int reload)
134 {
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;
140
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);
145                 return -1;
146         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
147                 return 0;
148         }
149
150         if (reload) {
151                 free_config();
152         }
153
154         if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
155                 int facility = ast_syslog_facility(tmp);
156                 if (facility < 0) {
157                         ast_log(AST_LOG_WARNING,
158                                 "Invalid facility '%s' specified, defaulting to '%s'\n",
159                                 tmp, ast_syslog_facility_name(default_facility));
160                 } else {
161                         default_facility = facility;
162                 }
163         }
164
165         if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
166                 int priority = ast_syslog_priority(tmp);
167                 if (priority < 0) {
168                         ast_log(AST_LOG_WARNING,
169                                 "Invalid priority '%s' specified, defaulting to '%s'\n",
170                                 tmp, ast_syslog_priority_name(default_priority));
171                 } else {
172                         default_priority = priority;
173                 }
174         }
175
176         while ((catg = ast_category_browse(cfg, catg))) {
177                 struct cdr_config *sink;
178
179                 if (!strcasecmp(catg, "general")) {
180                         continue;
181                 }
182
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);
186                         continue;
187                 }
188
189                 sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
190
191                 if (!sink) {
192                         ast_log(AST_LOG_ERROR,
193                                 "Unable to allocate memory for configuration settings.\n");
194                         free_config();
195                         break;
196                 }
197
198                 ast_mutex_init(&sink->lock);
199                 ast_string_field_set(sink, ident, catg);
200                 ast_string_field_set(sink, format, tmp);
201
202                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
203                         sink->facility = default_facility;
204                 } else {
205                         int facility = ast_syslog_facility(tmp);
206                         if (facility < 0) {
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));
210                         } else {
211                                 sink->facility = facility;
212                         }
213                 }
214
215                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
216                         sink->priority = default_priority;
217                 } else {
218                         int priority = ast_syslog_priority(tmp);
219                         if (priority < 0) {
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));
223                         } else {
224                                 sink->priority = priority;
225                         }
226                 }
227
228                 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
229         }
230
231         ast_config_destroy(cfg);
232
233         return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
234 }
235
236 static int unload_module(void)
237 {
238         ast_cdr_unregister(name);
239
240         if (AST_RWLIST_WRLOCK(&sinks)) {
241                 ast_cdr_register(name, ast_module_info->description, syslog_log);
242                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
243                 return -1;
244         }
245
246         free_config();
247         AST_RWLIST_UNLOCK(&sinks);
248         return 0;
249 }
250
251 static enum ast_module_load_result load_module(void)
252 {
253         int res;
254
255         if (AST_RWLIST_WRLOCK(&sinks)) {
256                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
257                 return AST_MODULE_LOAD_DECLINE;
258         }
259
260         res = load_config(0);
261         AST_RWLIST_UNLOCK(&sinks);
262         if (res) {
263                 return AST_MODULE_LOAD_DECLINE;
264         }
265         ast_cdr_register(name, ast_module_info->description, syslog_log);
266         return AST_MODULE_LOAD_SUCCESS;
267 }
268
269 static int reload(void)
270 {
271         int res;
272         if (AST_RWLIST_WRLOCK(&sinks)) {
273                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
274                 return AST_MODULE_LOAD_DECLINE;
275         }
276
277         if ((res = load_config(1))) {
278                 free_config();
279         }
280
281         AST_RWLIST_UNLOCK(&sinks);
282
283         return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
284 }
285
286 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
287         .load = load_module,
288         .unload = unload_module,
289         .reload = reload,
290         .load_pri = AST_MODPRI_CDR_DRIVER,
291 );