Asterisk - The Open Source Telephony Project  21.4.1
cel_beanstalkd.c
Go to the documentation of this file.
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  * \ingroup cel_drivers
28  */
29 
30 /*! \li \ref cek_beanstalkd.c uses the configuration file \ref cel.conf
31  * \addtogroup configuration_file Configuration Files
32  */
33 
34 /*!
35  * \page cel.conf cel.conf
36  * \verbinclude cel.conf.sample
37  */
38 
39 /*** MODULEINFO
40  <depend>beanstalk</depend>
41  <support_level>extended</support_level>
42  ***/
43 
44 #include "asterisk.h"
45 
46 #include "asterisk/channel.h"
47 #include "asterisk/cel.h"
48 #include "asterisk/module.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/config.h"
53 #include "asterisk/json.h"
54 
55 #include "beanstalk.h"
56 
57 static const char DATE_FORMAT[] = "%Y-%m-%d %T";
58 
59 static const char CONF_FILE[] = "cel_beanstalkd.conf";
60 
61 /*! \brief Beanstalk CEL is off by default */
62 #define CEL_BEANSTALK_ENABLED_DEFAULT 0
63 
64 static int enablecel;
65 
66 /*! \brief show_user_def is off by default */
67 #define CEL_SHOW_USERDEF_DEFAULT 0
68 
69 #define CEL_BACKEND_NAME "Beanstalk Event Logging"
70 
71 #define BEANSTALK_JOB_SIZE 4096
72 #define BEANSTALK_JOB_PRIORITY 99
73 #define BEANSTALK_JOB_TTR 60
74 #define BEANSTALK_JOB_DELAY 0
75 #define DEFAULT_BEANSTALK_HOST "127.0.0.1"
76 #define DEFAULT_BEANSTALK_PORT 11300
77 #define DEFAULT_BEANSTALK_TUBE "asterisk-cel"
78 
79 static char *bs_host;
80 static int bs_port;
81 static char *bs_tube;
82 static int priority;
83 
84 AST_RWLOCK_DEFINE_STATIC(config_lock);
85 
86 static void cel_bs_put(struct ast_event *event)
87 {
88  struct ast_tm timeresult;
89  char start_time[80];
90  char *cel_buffer;
91  int bs_id;
92  int bs_socket;
93  struct ast_json *t_cel_json;
94 
95  struct ast_cel_event_record record = {
97  };
98 
99  if (!enablecel) {
100  return;
101  }
102 
103  if (ast_cel_fill_record(event, &record)) {
104  return;
105  }
106 
107  ast_rwlock_rdlock(&config_lock);
108  bs_socket = bs_connect(bs_host, bs_port);
109 
110  if (bs_use(bs_socket, bs_tube) != BS_STATUS_OK) {
111  ast_log(LOG_ERROR, "Connection to Beanstalk tube %s @ %s:%d had failed", bs_tube, bs_host, bs_port);
112  ast_rwlock_unlock(&config_lock);
113  return;
114  }
115 
116  ast_localtime(&record.event_time, &timeresult, NULL);
117  ast_strftime(start_time, sizeof(start_time), DATE_FORMAT, &timeresult);
118 
119  ast_rwlock_unlock(&config_lock);
120 
121  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}",
122  "EventName", S_OR(record.event_name, ""),
123  "AccountCode", S_OR(record.account_code, ""),
124  "CallerIDnum", S_OR(record.caller_id_num, ""),
125  "CallerIDname", S_OR(record.caller_id_name, ""),
126  "CallerIDani", S_OR(record.caller_id_ani, ""),
127  "CallerIDrdnis", S_OR(record.caller_id_rdnis, ""),
128  "CallerIDdnid", S_OR(record.caller_id_dnid, ""),
129  "Exten", S_OR(record.extension, ""),
130  "Context", S_OR(record.context, ""),
131  "Channel", S_OR(record.channel_name, ""),
132  "Application", S_OR(record.application_name, ""),
133  "AppData", S_OR(record.application_data, ""),
134  "EventTime", S_OR(start_time, ""),
135  "AMAFlags", S_OR(ast_channel_amaflags2string(record.amaflag), ""),
136  "UniqueID", S_OR(record.unique_id, ""),
137  "LinkedID", S_OR(record.linked_id, ""),
138  "Userfield", S_OR(record.user_field, ""),
139  "Peer", S_OR(record.peer_account, ""),
140  "PeerAccount", S_OR(record.peer_account, ""),
141  "Extra", S_OR(record.extra, "")
142 
143  );
144 
145  cel_buffer = ast_json_dump_string(t_cel_json);
146 
147  ast_json_unref(t_cel_json);
148 
149  bs_id = bs_put(bs_socket, priority, BEANSTALK_JOB_DELAY, BEANSTALK_JOB_TTR, cel_buffer, strlen(cel_buffer));
150 
151  if (bs_id > 0) {
152  ast_log(LOG_DEBUG, "Successfully created job %d with %s\n", bs_id, cel_buffer);
153  } else {
154  ast_log(LOG_ERROR, "CDR job creation failed for %s\n", cel_buffer);
155  }
156 
157  bs_disconnect(bs_socket);
158  ast_json_free(cel_buffer);
159 }
160 
161 static int load_config(int reload)
162 {
163  const char *cat = NULL;
164  struct ast_config *cfg;
165  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
166  struct ast_variable *v;
167  int newenablecel = CEL_BEANSTALK_ENABLED_DEFAULT;
168 
169  cfg = ast_config_load(CONF_FILE, config_flags);
170  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
171  return 0;
172  }
173 
174  if (cfg == CONFIG_STATUS_FILEINVALID) {
175  ast_log(LOG_WARNING, "Configuration file '%s' is invalid. CEL Beanstalkd Module not activated.\n",
176  CONF_FILE);
177  return -1;
178  } else if (!cfg) {
179  ast_log(LOG_WARNING, "Failed to load configuration file. CEL Beanstalkd Module not activated.\n");
180  if (enablecel) {
181  ast_cel_backend_unregister(CEL_BACKEND_NAME);
182  }
183  enablecel = 0;
184  return -1;
185  }
186 
187  if (reload) {
188  ast_rwlock_wrlock(&config_lock);
189  ast_free(bs_host);
190  ast_free(bs_tube);
191  }
192 
193  /* Bootstrap the default configuration */
194  bs_host = ast_strdup(DEFAULT_BEANSTALK_HOST);
195  bs_port = DEFAULT_BEANSTALK_PORT;
196  bs_tube = ast_strdup(DEFAULT_BEANSTALK_TUBE);
197  priority = BEANSTALK_JOB_PRIORITY;
198 
199  while ((cat = ast_category_browse(cfg, cat))) {
200 
201  if (strcasecmp(cat, "general")) {
202  continue;
203  }
204 
205  for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
206  if (!strcasecmp(v->name, "enabled")) {
207  newenablecel = ast_true(v->value) ? 1 : 0;
208  } else if (!strcasecmp(v->name, "host")) {
209  ast_free(bs_host);
210  bs_host = ast_strdup(v->value);
211  } else if (!strcasecmp(v->name, "port")) {
212  bs_port = atoi(v->value);
213  } else if (!strcasecmp(v->name, "tube")) {
214  ast_free(bs_tube);
215  bs_tube = ast_strdup(v->value);
216  } else if (!strcasecmp(v->name, "priority")) {
217  priority = atoi(v->value);
218  } else {
219  ast_log(LOG_NOTICE, "Unknown option '%s' specified "
220  "for CEL beanstalk backend.\n", v->name);
221  }
222  }
223  }
224 
225  if (reload) {
226  ast_rwlock_unlock(&config_lock);
227  }
228 
229  ast_config_destroy(cfg);
230 
231  if (enablecel && !newenablecel) {
232  ast_cel_backend_unregister(CEL_BACKEND_NAME);
233  } else if (!enablecel && newenablecel) {
234  if (ast_cel_backend_register(CEL_BACKEND_NAME, cel_bs_put)) {
235  ast_log(LOG_ERROR, "Unable to register Beanstalkd CEL handling\n");
236  }
237  }
238 
239  enablecel = newenablecel;
240 
241  return 0;
242 }
243 
244 static int unload_module(void)
245 {
246  ast_cel_backend_unregister(CEL_BACKEND_NAME);
247  ast_free(bs_host);
248  ast_free(bs_tube);
249  return 0;
250 }
251 
252 static int load_module(void)
253 {
254  if (load_config(0)) {
256  }
257 
259 }
260 
261 static int reload(void)
262 {
263  return load_config(1);
264 }
265 
266 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Beanstalkd CEL Backend",
267  .support_level = AST_MODULE_SUPPORT_EXTENDED,
268  .load = load_module,
269  .unload = unload_module,
270  .reload = reload,
271  .load_pri = AST_MODPRI_CDR_DRIVER,
272  .requires = "cel",
273 );
struct ast_variable * next
Helper struct for getting the fields out of a CEL event.
Definition: cel.h:138
An event.
Definition: event.c:81
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
Call Event Logging API.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
Register a CEL backend.
Definition: cel.c:1781
#define CEL_BEANSTALK_ENABLED_DEFAULT
Beanstalk CEL is off by default.
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
Structure for variables, used for configurations and for channel variables.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:810
Definition: astman.c:222
const char * ast_channel_amaflags2string(enum ama_flags flags)
Convert the enum representation of an AMA flag to a string representation.
Definition: channel.c:4373
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
Utility functions.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
uint32_t version
struct ABI version
Definition: cel.h:148
int ast_cel_backend_unregister(const char *name)
Unregister a CEL backend.
Definition: cel.c:1769
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
#define DATE_FORMAT
Definition: app_meetme.c:662
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
#define AST_CEL_EVENT_RECORD_VERSION
struct ABI version
Definition: cel.h:143
Structure used to handle boolean flags.
Definition: utils.h:199
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
Abstract JSON element (object, array, string, int, ...).
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_cel_fill_record(const struct ast_event *event, struct ast_cel_event_record *r)
Fill in an ast_cel_event_record from a CEL event.
Definition: cel.c:821