Merge "BuildSystem: For consistency, avoid double-checking via if clauses."
[asterisk/asterisk.git] / cel / cel_beanstalkd.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2017, Greenfield Technologies Ltd.
5  *
6  * Nir Simionovich <nirs@greenfieldtech.net>
7  * who freely borrowed code from the cel manager equivalents
8  *     (see cel/cel_manager.c)
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief Asterisk Channel Event Beanstalkd backend
24  *
25  * This module requires the beanstalk-client library, avaialble from
26  * https://github.com/deepfryed/beanstalk-client
27  *
28  * See also
29  * \arg \ref AstCEL
30  * \ingroup cel_drivers
31  */
32
33 /*! \li \ref cek_beanstalkd.c uses the configuration file \ref cel.conf
34  * \addtogroup configuration_file Configuration Files
35  */
36
37 /*!
38  * \page cel.conf cel.conf
39  * \verbinclude cel.conf.sample
40  */
41
42 /*** MODULEINFO
43         <depend>beanstalk</depend>
44         <support_level>extended</support_level>
45  ***/
46
47 #include "asterisk.h"
48
49 #include "asterisk/channel.h"
50 #include "asterisk/cel.h"
51 #include "asterisk/module.h"
52 #include "asterisk/logger.h"
53 #include "asterisk/utils.h"
54 #include "asterisk/manager.h"
55 #include "asterisk/config.h"
56 #include "asterisk/json.h"
57
58 #include "beanstalk.h"
59
60 static const char DATE_FORMAT[] = "%Y-%m-%d %T";
61
62 static const char CONF_FILE[] = "cel_beanstalkd.conf";
63
64 /*! \brief Beanstalk CEL is off by default */
65 #define CEL_BEANSTALK_ENABLED_DEFAULT           0
66
67 static int enablecel;
68
69 /*! \brief show_user_def is off by default */
70 #define CEL_SHOW_USERDEF_DEFAULT        0
71
72 #define CEL_BACKEND_NAME "Beanstalk Event Logging"
73
74 #define BEANSTALK_JOB_SIZE 4096
75 #define BEANSTALK_JOB_PRIORITY 99
76 #define BEANSTALK_JOB_TTR 60
77 #define BEANSTALK_JOB_DELAY 0
78 #define DEFAULT_BEANSTALK_HOST "127.0.0.1"
79 #define DEFAULT_BEANSTALK_PORT 11300
80 #define DEFAULT_BEANSTALK_TUBE "asterisk-cel"
81
82 static char *bs_host;
83 static int bs_port;
84 static char *bs_tube;
85 static int priority;
86
87 AST_RWLOCK_DEFINE_STATIC(config_lock);
88
89 static void cel_bs_put(struct ast_event *event)
90 {
91         struct ast_tm timeresult;
92         char start_time[80];
93         char *cel_buffer;
94         int bs_id;
95         int bs_socket;
96         struct ast_json *t_cel_json;
97
98         struct ast_cel_event_record record = {
99                 .version = AST_CEL_EVENT_RECORD_VERSION,
100         };
101
102         if (!enablecel) {
103                 return;
104         }
105
106         if (ast_cel_fill_record(event, &record)) {
107                 return;
108         }
109
110         ast_rwlock_rdlock(&config_lock);
111         bs_socket = bs_connect(bs_host, bs_port);
112
113         if (bs_use(bs_socket, bs_tube) != BS_STATUS_OK) {
114                 ast_log(LOG_ERROR, "Connection to Beanstalk tube %s @ %s:%d had failed", bs_tube, bs_host, bs_port);
115                 ast_rwlock_unlock(&config_lock);
116                 return;
117         }
118
119         ast_localtime(&record.event_time, &timeresult, NULL);
120         ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult);
121
122         ast_rwlock_unlock(&config_lock);
123
124         t_cel_json = ast_json_pack("{s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s, s:s}",
125                                                            "EventName", S_OR(record.event_name, ""),
126                                                            "AccountCode", S_OR(record.account_code, ""),
127                                                            "CallerIDnum", S_OR(record.caller_id_num, ""),
128                                                            "CallerIDname", S_OR(record.caller_id_name, ""),
129                                                            "CallerIDani", S_OR(record.caller_id_ani, ""),
130                                                            "CallerIDrdnis", S_OR(record.caller_id_rdnis, ""),
131                                                            "CallerIDdnid", S_OR(record.caller_id_dnid, ""),
132                                                            "Exten", S_OR(record.extension, ""),
133                                                            "Context", S_OR(record.context, ""),
134                                                            "Channel", S_OR(record.channel_name, ""),
135                                                            "Application", S_OR(record.application_name, ""),
136                                                            "AppData", S_OR(record.application_data, ""),
137                                                            "EventTime", S_OR(start_time, ""),
138                                                            "AMAFlags", S_OR(ast_channel_amaflags2string(record.amaflag), ""),
139                                                            "UniqueID", S_OR(record.unique_id, ""),
140                                                            "LinkedID", S_OR(record.linked_id, ""),
141                                                            "Userfield", S_OR(record.user_field, ""),
142                                                            "Peer", S_OR(record.peer_account, ""),
143                                                            "PeerAccount", S_OR(record.peer_account, ""),
144                                                            "Extra", S_OR(record.extra, "")
145
146         );
147
148         cel_buffer = ast_json_dump_string(t_cel_json);
149
150         ast_json_unref(t_cel_json);
151
152         bs_id = bs_put(bs_socket, priority, BEANSTALK_JOB_DELAY, BEANSTALK_JOB_TTR, cel_buffer, strlen(cel_buffer));
153
154         if (bs_id > 0) {
155                 ast_log(LOG_DEBUG, "Successfully created job %d with %s\n", bs_id, cel_buffer);
156         } else {
157                 ast_log(LOG_ERROR, "CDR job creation failed for %s\n", cel_buffer);
158         }
159
160         bs_disconnect(bs_socket);
161         ast_json_free(cel_buffer);
162 }
163
164 static int load_config(int reload)
165 {
166         const char *cat = NULL;
167         struct ast_config *cfg;
168         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
169         struct ast_variable *v;
170         int newenablecel = CEL_BEANSTALK_ENABLED_DEFAULT;
171
172         cfg = ast_config_load(CONF_FILE, config_flags);
173         if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
174                 return 0;
175         }
176
177         if (cfg == CONFIG_STATUS_FILEINVALID) {
178                 ast_log(LOG_WARNING, "Configuration file '%s' is invalid. CEL Beanstalkd Module not activated.\n",
179                         CONF_FILE);
180                 return -1;
181         } else if (!cfg) {
182                 ast_log(LOG_WARNING, "Failed to load configuration file. CEL Beanstalkd Module not activated.\n");
183                 if (enablecel) {
184                         ast_cel_backend_unregister(CEL_BACKEND_NAME);
185                 }
186                 enablecel = 0;
187                 return -1;
188         }
189
190         if (reload) {
191                 ast_rwlock_wrlock(&config_lock);
192                 ast_free(bs_host);
193                 ast_free(bs_tube);
194         }
195
196         /* Bootstrap the default configuration */
197         bs_host = ast_strdup(DEFAULT_BEANSTALK_HOST);
198         bs_port = DEFAULT_BEANSTALK_PORT;
199         bs_tube = ast_strdup(DEFAULT_BEANSTALK_TUBE);
200         priority = BEANSTALK_JOB_PRIORITY;
201
202         while ((cat = ast_category_browse(cfg, cat))) {
203
204                 if (strcasecmp(cat, "general")) {
205                         continue;
206                 }
207
208                 for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
209                         if (!strcasecmp(v->name, "enabled")) {
210                                 newenablecel = ast_true(v->value) ? 1 : 0;
211                         } else if (!strcasecmp(v->name, "host")) {
212                                 ast_free(bs_host);
213                                 bs_host = ast_strdup(v->value);
214                         } else if (!strcasecmp(v->name, "port")) {
215                                 bs_port = atoi(v->value);
216                         } else if (!strcasecmp(v->name, "tube")) {
217                                 ast_free(bs_tube);
218                                 bs_tube = ast_strdup(v->value);
219                         } else if (!strcasecmp(v->name, "priority")) {
220                                 priority = atoi(v->value);
221                         } else {
222                                 ast_log(LOG_NOTICE, "Unknown option '%s' specified "
223                                                 "for CEL beanstalk backend.\n", v->name);
224                         }
225                 }
226         }
227
228         if (reload) {
229                 ast_rwlock_unlock(&config_lock);
230         }
231
232         ast_config_destroy(cfg);
233
234         if (enablecel && !newenablecel) {
235                 ast_cel_backend_unregister(CEL_BACKEND_NAME);
236         } else if (!enablecel && newenablecel) {
237                 if (ast_cel_backend_register(CEL_BACKEND_NAME, cel_bs_put)) {
238                         ast_log(LOG_ERROR, "Unable to register Beanstalkd CEL handling\n");
239                 }
240         }
241
242         enablecel = newenablecel;
243
244         return 0;
245 }
246
247 static int unload_module(void)
248 {
249         ast_cel_backend_unregister(CEL_BACKEND_NAME);
250         ast_free(bs_host);
251         ast_free(bs_tube);
252         return 0;
253 }
254
255 static int load_module(void)
256 {
257         if (load_config(0)) {
258                 return AST_MODULE_LOAD_DECLINE;
259         }
260
261         return AST_MODULE_LOAD_SUCCESS;
262 }
263
264 static int reload(void)
265 {
266         return load_config(1);
267 }
268
269 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Beanstalkd CEL Backend",
270         .support_level = AST_MODULE_SUPPORT_EXTENDED,
271         .load = load_module,
272         .unload = unload_module,
273         .reload = reload,
274         .load_pri = AST_MODPRI_CDR_DRIVER,
275 );