Remove unnecessary includes, formatting tweak
[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 /*! \file cdr_syslog.c
20  *
21  * \brief syslog CDR logger
22  * \author Sean Bright <sean@malleable.com>
23  *
24  * See also
25  * \arg \ref Config_cdr
26  * \ingroup cdr_drivers
27  */
28
29 /*** MODULEINFO
30          <depend>syslog</depend>
31 ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include "asterisk/module.h"
38 #include "asterisk/lock.h"
39 #include "asterisk/cdr.h"
40 #include "asterisk/pbx.h"
41
42 #include <syslog.h>
43
44 #include "asterisk/syslog.h"
45
46 static const char CONFIG[] = "cdr_syslog.conf";
47
48 AST_THREADSTORAGE(syslog_buf);
49
50 static const char name[] = "cdr-syslog";
51
52 struct cdr_config {
53         AST_DECLARE_STRING_FIELDS(
54                 AST_STRING_FIELD(ident);
55                 AST_STRING_FIELD(format);
56         );
57         int facility;
58         int priority;
59         ast_mutex_t lock;
60         AST_LIST_ENTRY(cdr_config) list;
61 };
62
63 static AST_RWLIST_HEAD_STATIC(sinks, cdr_config);
64
65 static void free_config(void)
66 {
67         struct cdr_config *sink;
68         while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
69                 ast_mutex_destroy(&sink->lock);
70                 ast_free(sink);
71         }
72 }
73
74 static int syslog_log(struct ast_cdr *cdr)
75 {
76         struct ast_channel *dummy;
77         struct ast_str *str;
78         struct cdr_config *sink;
79
80         /* Batching saves memory management here.  Otherwise, it's the same as doing an
81            allocation and free each time. */
82         if (!(str = ast_str_thread_get(&syslog_buf, 16))) {
83                 return -1;
84         }
85
86         if (!(dummy = ast_dummy_channel_alloc())) {
87                 ast_log(AST_LOG_ERROR, "Unable to allocate channel for variable substitution.\n");
88                 return -1;
89         }
90
91         /* We need to dup here since the cdr actually belongs to the other channel,
92            so when we release this channel we don't want the CDR getting cleaned
93            up prematurely. */
94         dummy->cdr = ast_cdr_dup(cdr);
95
96         AST_RWLIST_RDLOCK(&sinks);
97
98         AST_LIST_TRAVERSE(&sinks, sink, list) {
99
100                 ast_str_substitute_variables(&str, 0, dummy, sink->format);
101
102                 /* Even though we have a lock on the list, we could be being chased by
103                    another thread and this lock ensures that we won't step on anyone's
104                    toes.  Once each CDR backend gets it's own thread, this lock can be
105                    removed. */
106                 ast_mutex_lock(&sink->lock);
107
108                 openlog(sink->ident, LOG_CONS, sink->facility);
109                 syslog(sink->priority, "%s", ast_str_buffer(str));
110                 closelog();
111
112                 ast_mutex_unlock(&sink->lock);
113         }
114
115         AST_RWLIST_UNLOCK(&sinks);
116
117         ast_channel_release(dummy);
118
119         return 0;
120 }
121
122 static int load_config(int reload)
123 {
124         struct ast_config *cfg;
125         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
126         int default_facility = LOG_LOCAL4;
127         int default_priority = LOG_INFO;
128         const char *catg = NULL, *tmp;
129
130         cfg = ast_config_load(CONFIG, config_flags);
131         if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
132                 ast_log(AST_LOG_ERROR,
133                         "Unable to load %s. Not logging custom CSV CDRs to syslog.\n", CONFIG);
134                 return -1;
135         } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
136                 return 0;
137         }
138
139         if (reload) {
140                 free_config();
141         }
142
143         if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "facility")))) {
144                 int facility = ast_syslog_facility(tmp);
145                 if (facility < 0) {
146                         ast_log(AST_LOG_WARNING,
147                                 "Invalid facility '%s' specified, defaulting to '%s'\n",
148                                 tmp, ast_syslog_facility_name(default_facility));
149                 } else {
150                         default_facility = facility;
151                 }
152         }
153
154         if (!(ast_strlen_zero(tmp = ast_variable_retrieve(cfg, "general", "priority")))) {
155                 int priority = ast_syslog_priority(tmp);
156                 if (priority < 0) {
157                         ast_log(AST_LOG_WARNING,
158                                 "Invalid priority '%s' specified, defaulting to '%s'\n",
159                                 tmp, ast_syslog_priority_name(default_priority));
160                 } else {
161                         default_priority = priority;
162                 }
163         }
164
165         while ((catg = ast_category_browse(cfg, catg))) {
166                 struct cdr_config *sink;
167
168                 if (!strcasecmp(catg, "general")) {
169                         continue;
170                 }
171
172                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "template"))) {
173                         ast_log(AST_LOG_WARNING,
174                                 "No 'template' parameter found for '%s'.  Skipping.\n", catg);
175                         continue;
176                 }
177
178                 sink = ast_calloc_with_stringfields(1, struct cdr_config, 1024);
179
180                 if (!sink) {
181                         ast_log(AST_LOG_ERROR,
182                                 "Unable to allocate memory for configuration settings.\n");
183                         free_config();
184                         break;
185                 }
186
187                 ast_mutex_init(&sink->lock);
188                 ast_string_field_set(sink, ident, catg);
189                 ast_string_field_set(sink, format, tmp);
190
191                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "facility"))) {
192                         sink->facility = default_facility;
193                 } else {
194                         int facility = ast_syslog_facility(tmp);
195                         if (facility < 0) {
196                                 ast_log(AST_LOG_WARNING,
197                                         "Invalid facility '%s' specified for '%s,' defaulting to '%s'\n",
198                                         tmp, catg, ast_syslog_facility_name(default_facility));
199                         } else {
200                                 sink->facility = facility;
201                         }
202                 }
203
204                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "priority"))) {
205                         sink->priority = default_priority;
206                 } else {
207                         int priority = ast_syslog_priority(tmp);
208                         if (priority < 0) {
209                                 ast_log(AST_LOG_WARNING,
210                                         "Invalid priority '%s' specified for '%s,' defaulting to '%s'\n",
211                                         tmp, catg, ast_syslog_priority_name(default_priority));
212                         } else {
213                                 sink->priority = priority;
214                         }
215                 }
216
217                 AST_RWLIST_INSERT_TAIL(&sinks, sink, list);
218         }
219
220         ast_config_destroy(cfg);
221
222         return AST_RWLIST_EMPTY(&sinks) ? -1 : 0;
223 }
224
225 static int unload_module(void)
226 {
227         ast_cdr_unregister(name);
228
229         if (AST_RWLIST_WRLOCK(&sinks)) {
230                 ast_cdr_register(name, ast_module_info->description, syslog_log);
231                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
232                 return -1;
233         }
234
235         free_config();
236         AST_RWLIST_UNLOCK(&sinks);
237         return 0;
238 }
239
240 static enum ast_module_load_result load_module(void)
241 {
242         int res;
243
244         if (AST_RWLIST_WRLOCK(&sinks)) {
245                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
246                 return AST_MODULE_LOAD_DECLINE;
247         }
248
249         res = load_config(0);
250         AST_RWLIST_UNLOCK(&sinks);
251         if (res) {
252                 return AST_MODULE_LOAD_DECLINE;
253         }
254         ast_cdr_register(name, ast_module_info->description, syslog_log);
255         return AST_MODULE_LOAD_SUCCESS;
256 }
257
258 static int reload(void)
259 {
260         int res;
261         if (AST_RWLIST_WRLOCK(&sinks)) {
262                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
263                 return AST_MODULE_LOAD_DECLINE;
264         }
265
266         free_config();
267         res = load_config(1);
268         AST_RWLIST_UNLOCK(&sinks);
269
270         return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
271 }
272
273 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Customizable syslog CDR Backend",
274         .load = load_module,
275         .unload = unload_module,
276         .reload = reload,
277 );