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