samples: Canonicalize app names in extensions.conf.sample.
[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 #include "asterisk/module.h"
47 #include "asterisk/lock.h"
48 #include "asterisk/cdr.h"
49 #include "asterisk/pbx.h"
50
51 #include <syslog.h>
52
53 #include "asterisk/syslog.h"
54
55 static const char CONFIG[] = "cdr_syslog.conf";
56
57 AST_THREADSTORAGE(syslog_buf);
58
59 static const char name[] = "cdr-syslog";
60
61 struct cdr_syslog_config {
62         AST_DECLARE_STRING_FIELDS(
63                 AST_STRING_FIELD(ident);
64                 AST_STRING_FIELD(format);
65         );
66         int facility;
67         int priority;
68         ast_mutex_t lock;
69         AST_LIST_ENTRY(cdr_syslog_config) list;
70 };
71
72 static AST_RWLIST_HEAD_STATIC(sinks, cdr_syslog_config);
73
74 static void free_config(void)
75 {
76         struct cdr_syslog_config *sink;
77
78         while ((sink = AST_RWLIST_REMOVE_HEAD(&sinks, list))) {
79                 ast_mutex_destroy(&sink->lock);
80                 ast_string_field_free_memory(sink);
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_syslog_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_syslog_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_syslog_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         if (ast_cdr_unregister(name)) {
239                 return -1;
240         }
241
242         if (AST_RWLIST_WRLOCK(&sinks)) {
243                 ast_cdr_register(name, ast_module_info->description, syslog_log);
244                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Unload failed.\n");
245                 return -1;
246         }
247
248         free_config();
249         AST_RWLIST_UNLOCK(&sinks);
250         return 0;
251 }
252
253 static enum ast_module_load_result load_module(void)
254 {
255         int res;
256
257         if (AST_RWLIST_WRLOCK(&sinks)) {
258                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
259                 return AST_MODULE_LOAD_DECLINE;
260         }
261
262         res = load_config(0);
263         AST_RWLIST_UNLOCK(&sinks);
264         if (res) {
265                 return AST_MODULE_LOAD_DECLINE;
266         }
267         ast_cdr_register(name, ast_module_info->description, syslog_log);
268         return AST_MODULE_LOAD_SUCCESS;
269 }
270
271 static int reload(void)
272 {
273         int res;
274         if (AST_RWLIST_WRLOCK(&sinks)) {
275                 ast_log(AST_LOG_ERROR, "Unable to lock sink list.  Load failed.\n");
276                 return AST_MODULE_LOAD_DECLINE;
277         }
278
279         if ((res = load_config(1))) {
280                 free_config();
281         }
282
283         AST_RWLIST_UNLOCK(&sinks);
284
285         return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
286 }
287
288 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Customizable syslog CDR Backend",
289         .support_level = AST_MODULE_SUPPORT_CORE,
290         .load = load_module,
291         .unload = unload_module,
292         .reload = reload,
293         .load_pri = AST_MODPRI_CDR_DRIVER,
294 );