1 /*
2 * mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2014-2015, Grasshopper
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is mod_rayo for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is Grasshopper
20 * Portions created by the Initial Developer are Copyright (C)
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 * Chris Rienzo <chris.rienzo@grasshopper.com>
25 *
26 * rayo_cpa_detector.c -- Glue to normalize events from and to allow multiple instantiation of various detectors in FreeSWITCH
27 */
28
29 #include "rayo_cpa_detector.h"
30
31 static struct {
32 /** detectors supported by this module mapped by signal-type */
33 switch_hash_t *detectors;
34 /** synchronizes access to detectors */
35 switch_mutex_t *detectors_mutex;
36 } globals;
37
38 struct rayo_cpa_detector;
39
40 /**
41 * Detector definition
42 */
43 struct rayo_cpa_detector {
44 /** unique internal name of this detector */
45 const char *name;
46 /** detector ID */
47 const char *uuid;
48 /** start detection APP */
49 const char *start_app;
50 /** args to pass to start detection app */
51 const char *start_app_args;
52 /** stop detection APP */
53 const char *stop_app;
54 /** args to pass to stop detection app */
55 const char *stop_app_args;
56 /** (optional) name of header to get the signal type from */
57 const char *signal_type_header;
58 /** (optional) where to get the signal value from the event */
59 const char *signal_value_header;
60 /** (optional) where to get the signal duration from the event */
61 const char *signal_duration_header;
62 /** detector event to signal type mapping */
63 switch_hash_t *signal_type_map;
64 };
65
66 /**
67 * Detection state
68 */
69 struct rayo_cpa_detector_state {
70 /** reference count */
71 int refs;
72 };
73
74 /**
75 * Start detecting
76 * @param call_uuid call to detect signal on
77 * @param signal_ns namespace of signal to detect
78 * @param error_detail on failure, describes the error
79 * @return 1 if successful, 0 if failed
80 */
rayo_cpa_detector_start(const char * call_uuid,const char * signal_ns,const char ** error_detail)81 int rayo_cpa_detector_start(const char *call_uuid, const char *signal_ns, const char **error_detail)
82 {
83 struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns);
84 switch_core_session_t *session;
85 if (detector) {
86 if (zstr(detector->start_app)) {
87 /* nothing to do */
88 return 1;
89 }
90 session = switch_core_session_locate(call_uuid);
91 if (session) {
92 struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid);
93 if (detector_state) {
94 /* detector is already running */
95 detector_state->refs++;
96 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Start detector %s, refs = %d\n", detector->name, detector_state->refs);
97 switch_core_session_rwunlock(session);
98 return 1;
99 }
100 detector_state = switch_core_session_alloc(session, sizeof(*detector_state));
101 detector_state->refs = 1;
102 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Starting detector %s, refs = 1\n", detector->name);
103 switch_channel_set_private(switch_core_session_get_channel(session), detector->uuid, detector_state);
104 switch_core_session_execute_application_async(session, detector->start_app, zstr(detector->start_app_args) ? NULL : detector->start_app_args);
105 switch_core_session_rwunlock(session);
106 return 1;
107 } else {
108 *error_detail = "session gone";
109 return 0;
110 }
111 }
112 *error_detail = "detector not supported";
113 return 0;
114 }
115
116 /**
117 * Stop detecting
118 * @param call_uuid call to stop detecting signal on
119 * @param signal_ns name of signal to stop detecting
120 */
rayo_cpa_detector_stop(const char * call_uuid,const char * signal_ns)121 void rayo_cpa_detector_stop(const char *call_uuid, const char *signal_ns)
122 {
123 struct rayo_cpa_detector *detector = switch_core_hash_find(globals.detectors, signal_ns);
124 switch_core_session_t *session;
125 if (detector) {
126 if (zstr(detector->stop_app)) {
127 /* nothing to do */
128 return;
129 }
130 session = switch_core_session_locate(call_uuid);
131 if (session) {
132 struct rayo_cpa_detector_state *detector_state = switch_channel_get_private(switch_core_session_get_channel(session), detector->uuid);
133 if (detector_state) {
134 detector_state->refs--;
135 if (detector_state->refs < 0) {
136 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Stop detector %s refs = %d\n", detector->name, detector_state->refs);
137 } else {
138 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stop detector %s refs = %d\n", detector->name, detector_state->refs);
139 }
140 if (detector_state->refs == 0) {
141 /* nobody interested in detector events- shut it down */
142 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Stopping detector %s\n", detector->name);
143 switch_core_session_execute_application_async(session, detector->stop_app, zstr(detector->stop_app_args) ? NULL : detector->stop_app_args);
144 switch_channel_set_private(switch_core_session_get_channel(session), detector->uuid, NULL);
145 }
146 } else {
147 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Detector %s is already stopped\n", detector->name);
148 }
149 switch_core_session_rwunlock(session);
150 }
151 }
152 }
153
154 /**
155 * Handle event from detector
156 */
rayo_cpa_detector_event(switch_event_t * event)157 static void rayo_cpa_detector_event(switch_event_t *event)
158 {
159 struct rayo_cpa_detector *detector = (struct rayo_cpa_detector *)event->bind_user_data;
160 if (detector) {
161 const char *signal_type = "rayo_default";
162 if (!zstr(detector->signal_type_header)) {
163 signal_type = switch_event_get_header(event, detector->signal_type_header);
164 }
165 if (!zstr(signal_type)) {
166 signal_type = switch_core_hash_find(detector->signal_type_map, signal_type);
167 }
168 if (!zstr(signal_type)) {
169 switch_event_t *cpa_event;
170 const char *uuid = switch_event_get_header(event, "Unique-ID");
171 if (zstr(uuid)) {
172 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s %s event is missing call UUID!\n", detector->name, signal_type);
173 return;
174 }
175 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(uuid), SWITCH_LOG_DEBUG, "Got Rayo CPA event %s\n", signal_type);
176 if (switch_event_create_subclass(&cpa_event, SWITCH_EVENT_CUSTOM, "rayo::cpa") == SWITCH_STATUS_SUCCESS) {
177 switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "Unique-ID", uuid);
178 switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "detector-name", detector->name);
179 switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "detector-uuid", detector->uuid);
180 switch_event_add_header(cpa_event, SWITCH_STACK_BOTTOM, "signal-type", "%s%s:%s", RAYO_CPA_BASE, signal_type, RAYO_VERSION);
181 if (!zstr(detector->signal_value_header)) {
182 const char *value = switch_event_get_header(event, detector->signal_value_header);
183 if (!zstr(value)) {
184 switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "value", value);
185 }
186 }
187 if (!zstr(detector->signal_duration_header)) {
188 const char *duration = switch_event_get_header(event, detector->signal_duration_header);
189 if (!zstr(duration)) {
190 switch_event_add_header_string(cpa_event, SWITCH_STACK_BOTTOM, "duration", duration);
191 }
192 }
193 switch_event_fire(&cpa_event);
194 }
195 } else {
196 /* couldn't map event to Rayo signal-type */
197 const char *event_name = switch_event_get_header(event, "Event-Name");
198 const char *event_subclass = switch_event_get_header(event, "Event-Subclass");
199 if (zstr(event_subclass)) {
200 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to find Rayo signal-type for event %s\n", event_name);
201 } else {
202 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Failed to find Rayo signal-type for event %s %s\n", event_name, event_subclass);
203 }
204 }
205 }
206 }
207
208 #define RAYO_CPA_DETECTOR_SYNTAX "rayo_cpa <uuid> <signal-type> <start|stop>"
SWITCH_STANDARD_API(rayo_cpa_detector_api)209 SWITCH_STANDARD_API(rayo_cpa_detector_api)
210 {
211 char *cmd_dup = NULL;
212 char *argv[4] = { 0 };
213 int argc = 0;
214
215 if (zstr(cmd)) {
216 stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX);
217 goto done;
218 }
219
220 cmd_dup = strdup(cmd);
221 argc = switch_separate_string(cmd_dup, ' ', argv, sizeof(argv) / sizeof(argv[0]));
222
223 if (argc != 3) {
224 stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX);
225 } else {
226 const char *err_reason = NULL;
227 if (!strcmp(argv[2], "stop")) {
228 rayo_cpa_detector_stop(argv[0], argv[1]);
229 stream->write_function(stream, "+OK\n");
230 } else if (!strcmp(argv[2], "start")) {
231 if (!rayo_cpa_detector_start(argv[0], argv[1], &err_reason)) {
232 if (err_reason) {
233 stream->write_function(stream, "-ERR: %s\n", err_reason);
234 } else {
235 stream->write_function(stream, "-ERR\n");
236 }
237 } else {
238 stream->write_function(stream, "+OK\n");
239 }
240 } else {
241 stream->write_function(stream, "-ERR: USAGE %s\n", RAYO_CPA_DETECTOR_SYNTAX);
242 }
243 }
244
245 done:
246 switch_safe_free(cmd_dup);
247
248 return SWITCH_STATUS_SUCCESS;
249 }
250
251 /**
252 * Detector definition destructor
253 */
destroy_detector(void * ptr)254 static void destroy_detector(void *ptr)
255 {
256 struct rayo_cpa_detector *detector = (struct rayo_cpa_detector *) ptr;
257 if (detector->signal_type_map) {
258 switch_core_hash_destroy(&detector->signal_type_map);
259 }
260 }
261
262 /**
263 * Configure CPA
264 */
do_config(switch_memory_pool_t * pool,const char * config_file)265 static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_file)
266 {
267 switch_xml_t cfg, xml, cpa_xml;
268 switch_status_t status = SWITCH_STATUS_SUCCESS;
269 switch_hash_t *bound_events;
270
271 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Configuring CPA\n");
272 if (!(xml = switch_xml_open_cfg(config_file, &cfg, NULL))) {
273 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", config_file);
274 return SWITCH_STATUS_TERM;
275 }
276
277 switch_core_hash_init(&bound_events);
278
279 cpa_xml = switch_xml_child(cfg, "cpa");
280 if (cpa_xml) {
281 switch_xml_t detector_xml;
282
283 for (detector_xml = switch_xml_child(cpa_xml, "detector"); detector_xml; detector_xml = detector_xml->next) {
284 switch_xml_t start_xml, stop_xml, event_xml;
285 struct rayo_cpa_detector *detector;
286 char id[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
287 const char *name = switch_xml_attr_soft(detector_xml, "name");
288 if (zstr(name)) {
289 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing name of CPA detector!\n");
290 status = SWITCH_STATUS_TERM;
291 goto done;
292 }
293 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "CPA detector: %s\n", name);
294 detector = switch_core_alloc(pool, sizeof(*detector));
295 switch_core_hash_init(&detector->signal_type_map);
296 detector->name = switch_core_strdup(pool, name);
297 switch_uuid_str(id, sizeof(id));
298 detector->uuid = switch_core_strdup(pool, id);
299
300 start_xml = switch_xml_child(detector_xml, "start");
301 if (start_xml) {
302 detector->start_app = switch_core_strdup(pool, switch_xml_attr_soft(start_xml, "application"));
303 detector->start_app_args = switch_core_strdup(pool, switch_xml_attr_soft(start_xml, "data"));
304 }
305
306 stop_xml = switch_xml_child(detector_xml, "stop");
307 if (stop_xml) {
308 detector->stop_app = switch_core_strdup(pool, switch_xml_attr_soft(stop_xml, "application"));
309 detector->stop_app_args = switch_core_strdup(pool, switch_xml_attr_soft(stop_xml, "data"));
310 }
311
312 event_xml = switch_xml_child(detector_xml, "event");
313 if (event_xml) {
314 int event_ok = 0;
315 switch_xml_t signal_type_xml;
316 const char *event_class = switch_xml_attr_soft(event_xml, "class");
317 const char *event_subclass = switch_xml_attr_soft(event_xml, "subclass");
318 switch_event_types_t event_type;
319 if (zstr(event_class)) {
320 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing event class for CPA detector: %s\n", detector->name);
321 status = SWITCH_STATUS_TERM;
322 goto done;
323 }
324
325 if (switch_name_event(event_class, &event_type) != SWITCH_STATUS_SUCCESS) {
326 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event class %s for CPA detector: %s\n", event_class, detector->name);
327 status = SWITCH_STATUS_TERM;
328 goto done;
329 }
330
331 /* bind detector to event if not already done... */
332 {
333 struct rayo_cpa_detector *bound_detector;
334 const char *event_name = switch_core_sprintf(pool, "%s %s", event_class, event_subclass);
335 if (!(bound_detector = switch_core_hash_find(bound_events, event_name))) {
336 /* not yet bound */
337 if (zstr(event_subclass)) {
338 event_subclass = NULL;
339 }
340 switch_event_bind("rayo_cpa_detector", event_type, event_subclass, rayo_cpa_detector_event, detector);
341 switch_core_hash_insert(bound_events, event_name, detector); /* mark as bound */
342 } else if (bound_detector != detector) {
343 /* can't have multiple detectors generating the same event! */
344 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s attempted to bind to event %s that is already bound by %s\n", detector->name, event_name, bound_detector->name);
345 status = SWITCH_STATUS_TERM;
346 goto done;
347 }
348 }
349
350 /* configure native event -> rayo CPA event mapping */
351 detector->signal_type_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "type-header"));
352 detector->signal_value_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "value-header"));
353 detector->signal_duration_header = switch_core_strdup(pool, switch_xml_attr_soft(event_xml, "duration-header"));
354
355 /* configure native event type -> rayo CPA signal type mapping */
356 for (signal_type_xml = switch_xml_child(event_xml, "signal-type"); signal_type_xml; signal_type_xml = signal_type_xml->next) {
357 const char *header_value = switch_core_strdup(pool, switch_xml_attr_soft(signal_type_xml, "header-value"));
358 const char *signal_type = switch_core_strdup(pool, switch_xml_attr_soft(signal_type_xml, "value"));
359 if (zstr(signal_type)) {
360 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s missing signal-type value!\n", detector->name);
361 status = SWITCH_STATUS_TERM;
362 goto done;
363 } else {
364 /* add signal-type to detector mapping if not already done for this detector */
365 const char *signal_type_ns = switch_core_sprintf(pool, "%s%s:%s", RAYO_CPA_BASE, signal_type, RAYO_VERSION);
366 struct rayo_cpa_detector *bound_detector = switch_core_hash_find(globals.detectors, signal_type_ns);
367 if (!bound_detector) {
368 switch_core_hash_insert_destructor(globals.detectors, signal_type_ns, detector, destroy_detector);
369 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding CPA %s => %s\n", signal_type_ns, detector->name);
370 event_ok = 1;
371 } else if (bound_detector == detector) {
372 /* detector has multiple signal-type configs w/ same value */
373 event_ok = 1;
374 } else {
375 /* multiple detectors with same signal-type value */
376 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s configured to handle signal-type %s that is already handled by %s\n", detector->name, signal_type, bound_detector->name);
377 status = SWITCH_STATUS_TERM;
378 goto done;
379 }
380 }
381
382 /* map event value to signal-type */
383 if (zstr(header_value)) {
384 switch_core_hash_insert(detector->signal_type_map, "rayo_default", signal_type);
385 } else {
386 switch_core_hash_insert(detector->signal_type_map, header_value, signal_type);
387 }
388 }
389
390 if (!event_ok) {
391 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Detector %s is missing Rayo signal-type for event\n", detector->name);
392 status = SWITCH_STATUS_TERM;
393 goto done;
394 }
395 }
396 }
397 }
398
399 done:
400 switch_core_hash_destroy(&bound_events);
401 switch_xml_free(xml);
402
403 return status;
404 }
405
406 /**
407 * Console auto-completion for signal types
408 */
rayo_cpa_detector_signal_types(const char * line,const char * cursor,switch_console_callback_match_t ** matches)409 static switch_status_t rayo_cpa_detector_signal_types(const char *line, const char *cursor, switch_console_callback_match_t **matches)
410 {
411 switch_status_t status = SWITCH_STATUS_FALSE;
412 switch_hash_index_t *hi;
413 void *val;
414 const void *vvar;
415 switch_console_callback_match_t *my_matches = NULL;
416
417 switch_mutex_lock(globals.detectors_mutex);
418 for (hi = switch_core_hash_first(globals.detectors); hi; hi = switch_core_hash_next(&hi)) {
419 switch_core_hash_this(hi, &vvar, NULL, &val);
420 switch_console_push_match(&my_matches, (const char *) vvar);
421 }
422 switch_mutex_unlock(globals.detectors_mutex);
423
424 if (my_matches) {
425 *matches = my_matches;
426 status = SWITCH_STATUS_SUCCESS;
427 }
428
429 return status;
430 }
431
432 /**
433 * Load CPA signal detection features
434 * @param module_interface
435 * @param pool memory pool
436 * @param config_file
437 * @return SWITCH_STATUS_SUCCESS if successfully loaded
438 */
rayo_cpa_detector_load(switch_loadable_module_interface_t ** module_interface,switch_memory_pool_t * pool,const char * config_file)439 switch_status_t rayo_cpa_detector_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool, const char *config_file)
440 {
441 switch_api_interface_t *api_interface;
442
443 switch_core_hash_init(&globals.detectors);
444 switch_mutex_init(&globals.detectors_mutex, SWITCH_MUTEX_NESTED, pool);
445
446 if (do_config(pool, config_file) != SWITCH_STATUS_SUCCESS) {
447 return SWITCH_STATUS_TERM;
448 }
449
450 SWITCH_ADD_API(api_interface, "rayo_cpa", "Query rayo status", rayo_cpa_detector_api, RAYO_CPA_DETECTOR_SYNTAX);
451
452 switch_console_set_complete("add rayo_cpa ::console::list_uuid ::rayo_cpa::list_signal_types start");
453 switch_console_set_complete("add rayo_cpa ::console::list_uuid ::rayo_cpa::list_signal_types stop");
454 switch_console_add_complete_func("::rayo_cpa::list_signal_types", rayo_cpa_detector_signal_types);
455
456 return SWITCH_STATUS_SUCCESS;
457 }
458
459 /**
460 * Disable CPA signal detection features
461 */
rayo_cpa_detector_shutdown(void)462 void rayo_cpa_detector_shutdown(void)
463 {
464 switch_console_set_complete("del rayo_cpa");
465 switch_console_del_complete_func("::rayo_cpa::list_signal_types");
466 if (globals.detectors) {
467 switch_core_hash_destroy(&globals.detectors);
468 }
469 switch_event_unbind_callback(rayo_cpa_detector_event);
470 }
471
472
473 /* For Emacs:
474 * Local Variables:
475 * mode:c
476 * indent-tabs-mode:t
477 * tab-width:4
478 * c-basic-offset:4
479 * End:
480 * For VIM:
481 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
482 */
483