Merged revisions 279410 via svnmerge from
[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 /*** MODULEINFO
31          <depend>syslog</depend>
32 ***/
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/module.h"
39 #include "asterisk/lock.h"
40 #include "asterisk/cdr.h"
41 #include "asterisk/pbx.h"
42
43 #include <syslog.h>
44
45 #include "asterisk/syslog.h"
46
47 static const char CONFIG[] = "cdr_syslog.conf";
48
49 AST_THREADSTORAGE(syslog_buf);
50
51 static const char name[] = "cdr-syslog";
52
53 struct cdr_config {
54         AST_DECLARE_STRING_FIELDS(
55                 AST_STRING_FIELD(ident);
56                 AST_STRING_FIELD(format);
57         );
58         int facility;
59         int priority;
60         ast_mutex_t lock;
61         AST_LIST_ENTRY(cdr_config) list;
62 };
63
64 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
65
66 static void free_config(void)
67 {
68         struct cdr_config *sink;
69         while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
70                 ast_mutex_destroy(&sink->lock);
71                 ast_free(sink);
72         }
73 }
74
75 static int syslog_log(struct ast_cdr *cdr)
76 {
77         struct ast_channel *dummy;
78         struct ast_str *str;
79         struct cdr_config *sink;
80
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))) {
84                 return -1;
85         }
86
87         if (!(dummy = ast_dummy_channel_alloc())) {
88                 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
89                 return -1;
90         }
91
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
94            up prematurely. */
95         dummy->cdr = ast_cdr_dup(cdr);
96
97         AST_RWLIST_RDLOCK(&sinks);
98
99         AST_LIST_TRAVERSE(&sinks, sink, list) {
100
101                 ast_str_substitute_variables(&str, 0, dummy, sink->format);
102
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
106                    removed. */
107                 ast_mutex_lock(&sink->lock);
108
109                 openlog(sink->ident, LOG_CONS, sink->facility);
110                 syslog(sink->priority, "%s", ast_str_buffer(str));
111                 closelog();
112
113                 ast_mutex_unlock(&sink->lock);
114         }
115
116         AST_RWLIST_UNLOCK(&sinks);
117
118         ast_channel_release(dummy);
119
120         return 0;
121 }
122
123 static int load_config(int reload)
124 {
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;
130
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);
135                 return -1;
136         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
137                 return 0;
138         }
139
140         if (reload) {
141                 free_config();
142         }
143
144         if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
145                 int facility = ast_syslog_facility(tmp);
146                 if (facility < 0) {
147                         ast_log(AST_LOG_WARNING,
148                                 "Invalid facility '%s' specified, defaulting to '%s'\n",
149                                 tmp, ast_syslog_facility_name(default_facility));
150                 } else {
151                         default_facility = facility;
152                 }
153         }
154
155         if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
156                 int priority = ast_syslog_priority(tmp);
157                 if (priority < 0) {
158                         ast_log(AST_LOG_WARNING,
159                                 "Invalid priority '%s' specified, defaulting to '%s'\n",
160                                 tmp, ast_syslog_priority_name(default_priority));
161                 } else {
162                         default_priority = priority;
163                 }
164         }
165
166         while ((catg = ast_category_browse(cfg, catg))) {
167                 struct cdr_config *sink;
168
169                 if (!strcasecmp(catg, "general")) {
170                         continue;
171                 }
172
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);
176                         continue;
177                 }
178
179                 sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
180
181                 if (!sink) {
182                         ast_log(AST_LOG_ERROR,
183                                 "Unable to allocate memory for configuration settings.\n");
184                         free_config();
185                         break;
186                 }
187
188                 ast_mutex_init(&sink->lock);
189                 ast_string_field_set(sink, ident, catg);
190                 ast_string_field_set(sink, format, tmp);
191
192                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
193                         sink->facility = default_facility;
194                 } else {
195                         int facility = ast_syslog_facility(tmp);
196                         if (facility < 0) {
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));
200                         } else {
201                                 sink->facility = facility;
202                         }
203                 }
204
205                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
206                         sink->priority = default_priority;
207                 } else {
208                         int priority = ast_syslog_priority(tmp);
209                         if (priority < 0) {
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));
213                         } else {
214                                 sink->priority = priority;
215                         }
216                 }
217
218                 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
219         }
220
221         ast_config_destroy(cfg);
222
223         return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
224 }
225
226 static int unload_module(void)
227 {
228         ast_cdr_unregister(name);
229
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");
233                 return -1;
234         }
235
236         free_config();
237         AST_RWLIST_UNLOCK(&sinks);
238         return 0;
239 }
240
241 static enum ast_module_load_result load_module(void)
242 {
243         int res;
244
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;
248         }
249
250         res = load_config(0);
251         AST_RWLIST_UNLOCK(&sinks);
252         if (res) {
253                 return AST_MODULE_LOAD_DECLINE;
254         }
255         ast_cdr_register(name, ast_module_info->description, syslog_log);
256         return AST_MODULE_LOAD_SUCCESS;
257 }
258
259 static int reload(void)
260 {
261         int res;
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;
265         }
266
267         free_config();
268         res = load_config(1);
269         AST_RWLIST_UNLOCK(&sinks);
270
271         return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
272 }
273
274 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
275         .load = load_module,
276         .unload = unload_module,
277         .reload = reload,
278         .load_pri = AST_MODPRI_CDR_DRIVER,
279 );