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