FS-7774 add mod_kazoo

#resolve
This commit is contained in:
karl anderson 2015-04-02 18:46:23 -04:00 committed by Luis Azedo
parent 0244af22e6
commit d66839debe
12 changed files with 4664 additions and 0 deletions

View File

@ -105,6 +105,7 @@ event_handlers/mod_event_socket
#event_handlers/mod_json_cdr
#event_handlers/mod_radius_cdr
#event_handlers/mod_odbc_cdr
#event_handlers/mod_kazoo
#event_handlers/mod_rayo
#event_handlers/mod_smpp
#event_handlers/mod_snmp

View File

@ -1772,6 +1772,7 @@ AC_CONFIG_FILES([Makefile
src/mod/event_handlers/mod_event_test/Makefile
src/mod/event_handlers/mod_format_cdr/Makefile
src/mod/event_handlers/mod_json_cdr/Makefile
src/mod/event_handlers/mod_kazoo/Makefile
src/mod/event_handlers/mod_radius_cdr/Makefile
src/mod/event_handlers/mod_odbc_cdr/Makefile
src/mod/event_handlers/mod_rayo/Makefile

View File

@ -0,0 +1,7 @@
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_kazoo
mod_LTLIBRARIES = mod_kazoo.la
mod_kazoo_la_SOURCES = mod_kazoo.c kazoo_utils.c kazoo_node.c kazoo_event_stream.c kazoo_fetch_agent.c kazoo_commands.c kazoo_dptools.c
mod_kazoo_la_CFLAGS = $(AM_CFLAGS) @ERLANG_CFLAGS@ -D_REENTRANT
mod_kazoo_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_kazoo_la_LDFLAGS = -avoid-version -module -no-undefined -shared @ERLANG_LDFLAGS@

View File

@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration name="kazoo.conf" description="General purpose Erlang c-node produced to better fit the Kazoo project">
<settings>
<param name="listen-ip" value="0.0.0.0" />
<param name="listen-port" value="8031" />
<!--<param name="cookie-file" value="/etc/freeswitch/autoload_configs/.erlang.cookie" />-->
<param name="cookie" value="change_me" />
<param name="shortname" value="false" />
<param name="nodename" value="freeswitch" />
<param name="send-msg-batch-size" value="10" />
<param name="receive-timeout" value="1" />
<!--<param name="receive-msg-preallocate" value="0" />-->
<!--<param name="event-stream-preallocate" value="0" />-->
<!--<param name="event-stream-framing" value="2" />-->
<!--<param name="kazoo-var-prefix" value="ecallmgr" />-->
<!--<param name="compat-rel" value="12"/> -->
</settings>
<event-filter type="whitelist">
<header name="Acquired-UUID" />
<header name="action" />
<header name="Action" />
<header name="alt_event_type" />
<header name="Answer-State" />
<header name="Application" />
<header name="Application-Data" />
<header name="Application-Name" />
<header name="Application-Response" />
<header name="att_xfer_replaced_by" />
<header name="Auth-Method" />
<header name="Auth-Realm" />
<header name="Auth-User" />
<header name="Bridge-A-Unique-ID" />
<header name="Bridge-B-Unique-ID" />
<header name="Call-Direction" />
<header name="Caller-Callee-ID-Name" />
<header name="Caller-Callee-ID-Number" />
<header name="Caller-Caller-ID-Name" />
<header name="Caller-Caller-ID-Number" />
<header name="Caller-Context" />
<header name="Caller-Controls" />
<header name="Caller-Destination-Number" />
<header name="Caller-Dialplan" />
<header name="Caller-Network-Addr" />
<header name="Caller-Unique-ID" />
<header name="Call-ID" />
<header name="Channel-Call-State" />
<header name="Channel-Call-UUID" />
<header name="Channel-Presence-ID" />
<header name="Channel-State" />
<header name="Chat-Permissions" />
<header name="Conference-Name" />
<header name="Conference-Profile-Name" />
<header name="Conference-Unique-ID" />
<header name="Conference-Size" />
<header name="New-ID" />
<header name="Old-ID" />
<header name="Detected-Tone" />
<header name="dialog_state" />
<header name="direction" />
<header name="Distributed-From" />
<header name="DTMF-Digit" />
<header name="DTMF-Duration" />
<header name="Event-Date-Timestamp" />
<header name="Event-Name" />
<header name="Event-Subclass" />
<header name="Expires" />
<header name="Ext-SIP-IP" />
<header name="File" />
<header name="FreeSWITCH-Hostname" />
<header name="from" />
<header name="Hunt-Destination-Number" />
<header name="ip" />
<header name="Message-Account" />
<header name="metadata" />
<header name="old_node_channel_uuid" />
<header name="Other-Leg-Callee-ID-Name" />
<header name="Other-Leg-Callee-ID-Number" />
<header name="Other-Leg-Caller-ID-Name" />
<header name="Other-Leg-Caller-ID-Number" />
<header name="Other-Leg-Destination-Number" />
<header name="Other-Leg-Direction" />
<header name="Other-Leg-Unique-ID" />
<header name="Participant-Type" />
<header name="Path" />
<header name="profile_name" />
<header name="Profiles" />
<header name="proto-specific-event-name" />
<header name="Raw-Application-Data" />
<header name="Resigning-UUID" />
<header name="set" />
<header name="sip_auto_answer" />
<header name="sip_auth_method" />
<header name="sip_from_host" />
<header name="sip_from_user" />
<header name="sip_to_host" />
<header name="sip_to_user" />
<header name="sub-call-id" />
<header name="technology" />
<header name="to" />
<header name="Unique-ID" />
<header name="URL" />
<header name="variable_channel_is_moving" />
<header name="variable_collected_digits" />
<header name="variable_current_application" />
<header name="variable_current_application_data" />
<header name="variable_domain_name" />
<header name="variable_effective_caller_id_name" />
<header name="variable_effective_caller_id_number" />
<header name="variable_fax_bad_rows" />
<header name="variable_fax_document_total_pages" />
<header name="variable_fax_document_transferred_pages" />
<header name="variable_fax_ecm_used" />
<header name="variable_fax_result_code" />
<header name="variable_fax_result_text" />
<header name="variable_fax_success" />
<header name="variable_fax_transfer_rate" />
<header name="variable_holding_uuid" />
<header name="variable_hold_music" />
<header name="variable_media_group_id" />
<header name="variable_originate_disposition" />
<header name="variable_playback_terminator_used" />
<header name="variable_presence_id" />
<header name="variable_record_ms" />
<header name="variable_recovered" />
<header name="variable_silence_hits_exhausted" />
<header name="variable_sip_auth_realm" />
<header name="variable_sip_from_host" />
<header name="variable_sip_from_user" />
<header name="variable_sip_h_X-AUTH-IP" />
<header name="variable_sip_received_ip" />
<header name="variable_sip_to_host" />
<header name="variable_sip_to_user" />
<header name="variable_sofia_profile_name" />
<header name="variable_transfer_history" />
<header name="variable_user_name" />
<header name="variable_endpoint_disposition" />
<header name="variable_originate_disposition" />
<header name="variable_bridge_hangup_cause" />
<header name="variable_hangup_cause" />
<header name="variable_last_bridge_proto_specific_hangup_cause" />
<header name="variable_proto_specific_hangup_cause" />
<header name="VM-Call-ID" />
<header name="VM-sub-call-id" />
<header name="whistle_application_name" />
<header name="whistle_application_response" />
<header name="whistle_event_name" />
<header name="sip_auto_answer_notify" />
<header name="eavesdrop_group" />
<header name="origination_caller_id_name" />
<header name="origination_caller_id_number" />
<header name="origination_callee_id_name" />
<header name="origination_callee_id_number" />
<header name="sip_auth_username" />
<header name="sip_auth_password" />
<header name="effective_caller_id_name" />
<header name="effective_caller_id_number" />
<header name="effective_callee_id_name" />
<header name="effective_callee_id_number" />
<!-- Registrations -->
<header name="call-id" />
<header name="profile-name" />
<header name="from-user" />
<header name="from-host" />
<header name="presence-hosts" />
<header name="contact" />
<header name="rpid" />
<header name="status" />
<header name="expires" />
<header name="to-user" />
<header name="to-host" />
<header name="network-ip" />
<header name="network-port" />
<header name="username" />
<header name="realm" />
<header name="user-agent" />
<!-- CDR Headers -->
<header name="Hangup-Cause" />
<header name="Unique-ID" />
<header name="variable_switch_r_sdp" />
<header name="variable_sip_local_sdp_str" />
<header name="variable_sip_to_uri" />
<header name="variable_sip_from_uri" />
<header name="variable_effective_caller_id_number" />
<header name="Caller-Caller-ID-Number" />
<header name="variable_effective_caller_id_name" />
<header name="Caller-Caller-ID-Name" />
<header name="Caller-Callee-ID-Name" />
<header name="Caller-Callee-ID-Number" />
<header name="Other-Leg-Unique-ID" />
<header name="variable_sip_user_agent" />
<header name="variable_duration" />
<header name="variable_billsec" />
<header name="variable_progresssec" />
<header name="variable_progress_uepoch" />
<header name="variable_progress_media_uepoch" />
<header name="variable_start_uepoch" />
<header name="variable_digits_dialed" />
<header name="variable_sip_cid_type" />
<!-- Conference Headers -->
<header name="Hear" />
<header name="Speak" />
<header name="Video" />
<header name="Talking" />
<header name="Mute-Detect" />
<header name="Member-ID" />
<header name="Member-Type" />
<header name="Energy-Level" />
<header name="Current-Energy" />
<header name="Floor" />
</event-filter>
</configuration>

View File

@ -0,0 +1,152 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Karl Anderson <karl@2600hz.com>
* Darren Schreiber <darren@2600hz.com>
*
*
* kazoo_commands.c -- clones of mod_commands commands slightly modified for kazoo
*
*/
#include "mod_kazoo.h"
#define UUID_SET_DESC "Set a variable"
#define UUID_SET_SYNTAX "<uuid> <var> [value]"
#define UUID_MULTISET_DESC "Set multiple variables"
#define UUID_MULTISET_SYNTAX "<uuid> <var>=<value>;<var>=<value>..."
SWITCH_STANDARD_API(uuid_setvar_function) {
switch_core_session_t *psession = NULL;
char *mycmd = NULL, *argv[3] = { 0 };
int argc = 0;
if (!zstr(cmd) && (mycmd = strdup(cmd))) {
argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
if ((argc == 2 || argc == 3) && !zstr(argv[0])) {
char *uuid = argv[0];
char *var_name = argv[1];
char *var_value = NULL;
if (argc == 3) {
var_value = argv[2];
}
if ((psession = switch_core_session_locate(uuid))) {
switch_channel_t *channel;
switch_event_t *event;
channel = switch_core_session_get_channel(psession);
if (zstr(var_name)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n");
stream->write_function(stream, "-ERR No variable specified\n");
} else {
switch_channel_set_variable(channel, var_name, var_value);
stream->write_function(stream, "+OK\n");
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(psession);
} else {
stream->write_function(stream, "-ERR No such channel!\n");
}
goto done;
}
}
stream->write_function(stream, "-USAGE: %s\n", UUID_SET_SYNTAX);
done:
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_STANDARD_API(uuid_setvar_multi_function) {
switch_core_session_t *psession = NULL;
char *mycmd = NULL, *vars, *argv[64] = { 0 };
int argc = 0;
char *var_name, *var_value = NULL;
if (!zstr(cmd) && (mycmd = strdup(cmd))) {
char *uuid = mycmd;
if (!(vars = strchr(uuid, ' '))) {
goto done;
}
*vars++ = '\0';
if ((psession = switch_core_session_locate(uuid))) {
switch_channel_t *channel = switch_core_session_get_channel(psession);
switch_event_t *event;
int x, y = 0;
argc = switch_separate_string(vars, ';', argv, (sizeof(argv) / sizeof(argv[0])));
for (x = 0; x < argc; x++) {
var_name = argv[x];
if (var_name && (var_value = strchr(var_name, '='))) {
*var_value++ = '\0';
}
if (zstr(var_name)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n");
stream->write_function(stream, "-ERR No variable specified\n");
} else {
switch_channel_set_variable(channel, var_name, var_value);
y++;
}
}
/* keep kazoo nodes in sync */
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
switch_core_session_rwunlock(psession);
if (y) {
stream->write_function(stream, "+OK\n");
goto done;
}
} else {
stream->write_function(stream, "-ERR No such channel!\n");
}
}
stream->write_function(stream, "-USAGE: %s\n", UUID_MULTISET_SYNTAX);
done:
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
void add_kz_commands(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface) {
SWITCH_ADD_API(api_interface, "kz_uuid_setvar_multi", UUID_SET_DESC, uuid_setvar_multi_function, UUID_MULTISET_SYNTAX);
switch_console_set_complete("add kz_uuid_setvar_multi ::console::list_uuid");
SWITCH_ADD_API(api_interface, "kz_uuid_setvar", UUID_MULTISET_DESC, uuid_setvar_function, UUID_SET_SYNTAX);
switch_console_set_complete("add kz_uuid_setvar ::console::list_uuid");
}

View File

@ -0,0 +1,182 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Karl Anderson <karl@2600hz.com>
* Darren Schreiber <darren@2600hz.com>
*
*
* kazoo_dptools.c -- clones of mod_dptools commands slightly modified for kazoo
*
*/
#include "mod_kazoo.h"
#define SET_SHORT_DESC "Set a channel variable"
#define SET_LONG_DESC "Set a channel variable for the channel calling the application."
#define SET_SYNTAX "<varname>=<value>"
#define MULTISET_SHORT_DESC "Set many channel variables"
#define MULTISET_LONG_DESC "Set many channel variables for the channel calling the application"
#define MULTISET_SYNTAX "[^^<delim>]<varname>=<value> <var2>=<val2>"
#define UNSET_SHORT_DESC "Unset a channel variable"
#define UNSET_LONG_DESC "Unset a channel variable for the channel calling the application."
#define UNSET_SYNTAX "<varname>"
#define MULTIUNSET_SHORT_DESC "Unset many channel variables"
#define MULTIUNSET_LONG_DESC "Unset many channel variables for the channel calling the application."
#define MULTIUNSET_SYNTAX "[^^<delim>]<varname> <var2> <var3>"
static void base_set (switch_core_session_t *session, const char *data, switch_stack_t stack) {
char *var, *val = NULL;
if (zstr(data)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n");
} else {
switch_channel_t *channel = switch_core_session_get_channel(session);
char *expanded = NULL;
var = switch_core_session_strdup(session, data);
if (!(val = strchr(var, '='))) {
val = strchr(var, ',');
}
if (val) {
*val++ = '\0';
if (zstr(val)) {
val = NULL;
}
}
if (val) {
expanded = switch_channel_expand_variables(channel, val);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s SET [%s]=[%s]\n", switch_channel_get_name(channel), var,
expanded ? expanded : "UNDEF");
switch_channel_add_variable_var_check(channel, var, expanded, SWITCH_FALSE, stack);
if (expanded && expanded != val) {
switch_safe_free(expanded);
}
}
}
SWITCH_STANDARD_APP(multiset_function) {
char delim = ' ';
char *arg = (char *) data;
switch_event_t *event;
if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') {
arg += 2;
delim = *arg++;
}
if (arg) {
switch_channel_t *channel = switch_core_session_get_channel(session);
char *array[256] = {0};
int i, argc;
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
for(i = 0; i < argc; i++) {
base_set(session, array[i], SWITCH_STACK_BOTTOM);
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
} else {
base_set(session, data, SWITCH_STACK_BOTTOM);
}
}
SWITCH_STANDARD_APP(set_function) {
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_t *event;
base_set(session, data, SWITCH_STACK_BOTTOM);
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
}
SWITCH_STANDARD_APP(unset_function) {
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_event_t *event;
if (zstr(data)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No variable name specified.\n");
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "UNSET [%s]\n", (char *) data);
switch_channel_set_variable(switch_core_session_get_channel(session), data, NULL);
}
if (switch_event_create(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) {
switch_channel_event_set_data(channel, event);
switch_event_fire(&event);
}
}
SWITCH_STANDARD_APP(multiunset_function) {
char delim = ' ';
char *arg = (char *) data;
if (!zstr(arg) && *arg == '^' && *(arg+1) == '^') {
arg += 2;
delim = *arg++;
}
if (arg) {
char *array[256] = {0};
int i, argc;
arg = switch_core_session_strdup(session, arg);
argc = switch_split(arg, delim, array);
for(i = 0; i < argc; i++) {
switch_channel_set_variable(switch_core_session_get_channel(session), array[i], NULL);
}
} else {
switch_channel_set_variable(switch_core_session_get_channel(session), arg, NULL);
}
}
void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switch_application_interface_t *app_interface) {
SWITCH_ADD_APP(app_interface, "kz_set", SET_SHORT_DESC, SET_LONG_DESC, set_function, SET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_multiset", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiset_function, MULTISET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_unset", UNSET_SHORT_DESC, UNSET_LONG_DESC, unset_function, UNSET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
SWITCH_ADD_APP(app_interface, "kz_multiunset", MULTISET_SHORT_DESC, MULTISET_LONG_DESC, multiunset_function, MULTIUNSET_SYNTAX,
SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC);
}

View File

@ -0,0 +1,584 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Karl Anderson <karl@2600hz.com>
* Darren Schreiber <darren@2600hz.com>
*
*
* kazoo_event_streams.c -- Event Publisher
*
*/
#include "mod_kazoo.h"
/* Blatantly repurposed from switch_eventc */
static char *my_dup(const char *s) {
size_t len = strlen(s) + 1;
void *new = malloc(len);
switch_assert(new);
return (char *) memcpy(new, s, len);
}
#ifndef DUP
#define DUP(str) my_dup(str)
#endif
static const char* private_headers[] = {"variable_sip_h_", "sip_h_", "P-", "X-"};
static int is_private_header(const char *name) {
for(int i=0; i < 4; i++) {
if(!strncmp(name, private_headers[i], strlen(private_headers[i]))) {
return 1;
}
}
return 0;
}
static switch_status_t kazoo_event_dup(switch_event_t **clone, switch_event_t *event, switch_hash_t *filter) {
switch_event_header_t *header;
if (switch_event_create_subclass(clone, SWITCH_EVENT_CLONE, event->subclass_name) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_GENERR;
}
(*clone)->event_id = event->event_id;
(*clone)->event_user_data = event->event_user_data;
(*clone)->bind_user_data = event->bind_user_data;
(*clone)->flags = event->flags;
for (header = event->headers; header; header = header->next) {
if (event->subclass_name && !strcmp(header->name, "Event-Subclass")) {
continue;
}
if (strncmp(header->name, globals.kazoo_var_prefix, globals.var_prefix_length)
&& filter
&& !switch_core_hash_find(filter, header->name)
&& (!globals.send_all_headers)
&& (!(globals.send_all_private_headers && is_private_header(header->name)))
)
{
continue;
}
if (header->idx) {
int i;
for (i = 0; i < header->idx; i++) {
switch_event_add_header_string(*clone, SWITCH_STACK_PUSH, header->name, header->array[i]);
}
} else {
switch_event_add_header_string(*clone, SWITCH_STACK_BOTTOM, header->name, header->value);
}
}
if (event->body) {
(*clone)->body = DUP(event->body);
}
(*clone)->key = event->key;
return SWITCH_STATUS_SUCCESS;
}
static void event_handler(switch_event_t *event) {
switch_event_t *clone = NULL;
ei_event_stream_t *event_stream = (ei_event_stream_t *) event->bind_user_data;
/* if mod_kazoo or the event stream isn't running dont push a new event */
if (!switch_test_flag(event_stream, LFLAG_RUNNING) || !switch_test_flag(&globals, LFLAG_RUNNING)) {
return;
}
if (event->event_id == SWITCH_EVENT_CUSTOM) {
ei_event_binding_t *event_binding = event_stream->bindings;
unsigned short int found = 0;
if (!event->subclass_name) {
return;
}
while(event_binding != NULL) {
if (event_binding->type == SWITCH_EVENT_CUSTOM) {
if(event_binding->subclass_name
&& !strcmp(event->subclass_name, event_binding->subclass_name)) {
found = 1;
break;
}
}
event_binding = event_binding->next;
}
if (!found) {
return;
}
}
/* try to clone the event and push it to the event stream thread */
/* TODO: someday maybe the filter comes from the event_stream (set during init only)
* and is per-binding so we only send headers that a process requests */
if (kazoo_event_dup(&clone, event, globals.event_filter) == SWITCH_STATUS_SUCCESS) {
if (switch_queue_trypush(event_stream->queue, clone) != SWITCH_STATUS_SUCCESS) {
/* if we couldn't place the cloned event into the listeners */
/* event queue make sure we destroy it, real good like */
switch_event_destroy(&clone);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory error: Have a good trip? See you next fall!\n");
}
}
static void *SWITCH_THREAD_FUNC event_stream_loop(switch_thread_t *thread, void *obj) {
ei_event_stream_t *event_stream = (ei_event_stream_t *) obj;
ei_event_binding_t *event_binding;
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[25];
const char *ip_addr;
void *pop;
short event_stream_framing = globals.event_stream_framing;
switch_atomic_inc(&globals.threads);
switch_assert(event_stream != NULL);
/* figure out what socket we just opened */
switch_socket_addr_get(&sa, SWITCH_FALSE, event_stream->acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof(ipbuf), sa);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting erlang event stream %p on %s:%u for %s <%d.%d.%d>\n"
,(void *)event_stream, ip_addr, port, event_stream->pid.node, event_stream->pid.creation
,event_stream->pid.num, event_stream->pid.serial);
while (switch_test_flag(event_stream, LFLAG_RUNNING) && switch_test_flag(&globals, LFLAG_RUNNING)) {
const switch_pollfd_t *fds;
int32_t numfds;
/* check if a new connection is pending */
if (switch_pollset_poll(event_stream->pollset, 0, &numfds, &fds) == SWITCH_STATUS_SUCCESS) {
for (int32_t i = 0; i < numfds; i++) {
switch_socket_t *newsocket;
/* accept the new client connection */
if (switch_socket_accept(&newsocket, event_stream->acceptor, event_stream->pool) == SWITCH_STATUS_SUCCESS) {
switch_sockaddr_t *sa;
if (switch_socket_opt_set(newsocket, SWITCH_SO_NONBLOCK, TRUE)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't set socket as non-blocking\n");
}
if (switch_socket_opt_set(newsocket, SWITCH_SO_TCP_NODELAY, 1)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't disable Nagle.\n");
}
/* close the current client, if there is one */
close_socket(&event_stream->socket);
switch_mutex_lock(event_stream->socket_mutex);
/* start sending to the new client */
event_stream->socket = newsocket;
switch_socket_addr_get(&sa, SWITCH_TRUE, newsocket);
event_stream->local_port = switch_sockaddr_get_port(sa);
switch_get_addr(event_stream->remote_ip, sizeof (event_stream->remote_ip), sa);
switch_socket_addr_get(&sa, SWITCH_FALSE, newsocket);
event_stream->remote_port = switch_sockaddr_get_port(sa);
switch_get_addr(event_stream->local_ip, sizeof (event_stream->local_ip), sa);
event_stream->connected = SWITCH_TRUE;
switch_mutex_unlock(event_stream->socket_mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang event stream %p client %s:%u\n", (void *)event_stream, event_stream->remote_ip, event_stream->remote_port);
}
}
}
/* if there was an event waiting in our queue send it to the client */
if (switch_queue_pop_timeout(event_stream->queue, &pop, 500000) == SWITCH_STATUS_SUCCESS) {
switch_event_t *event = (switch_event_t *) pop;
if (event_stream->socket) {
ei_x_buff ebuf;
char byte;
short i = event_stream_framing;
switch_size_t size = 1;
if(globals.event_stream_preallocate > 0) {
ebuf.buff = malloc(globals.event_stream_preallocate);
ebuf.buffsz = globals.event_stream_preallocate;
ebuf.index = 0;
if(ebuf.buff == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not pre-allocate memory for mod_kazoo message\n");
break;
}
ei_x_encode_version(&ebuf);
} else {
ei_x_new_with_version(&ebuf);
}
ei_encode_switch_event(&ebuf, event);
if (globals.event_stream_preallocate > 0 && ebuf.buffsz > globals.event_stream_preallocate) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "increased event stream buffer size to %d\n", ebuf.buffsz);
}
while (i) {
byte = ebuf.index >> (8 * --i);
switch_socket_send(event_stream->socket, &byte, &size);
}
size = (switch_size_t)ebuf.index;
switch_socket_send(event_stream->socket, ebuf.buff, &size);
ei_x_free(&ebuf);
}
switch_event_destroy(&event);
}
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutting down erlang event stream %p\n", (void *)event_stream);
/* unbind from the system events */
event_binding = event_stream->bindings;
while(event_binding != NULL) {
switch_event_unbind(&event_binding->node);
event_binding = event_binding->next;
}
event_stream->bindings = NULL;
/* clear and destroy any remaining queued events */
while (switch_queue_trypop(event_stream->queue, &pop) == SWITCH_STATUS_SUCCESS) {
switch_event_t *event = (switch_event_t *) pop;
switch_event_destroy(&event);
}
/* remove the acceptor pollset */
switch_pollset_remove(event_stream->pollset, event_stream->pollfd);
/* close any open sockets */
close_socket(&event_stream->acceptor);
switch_mutex_lock(event_stream->socket_mutex);
event_stream->connected = SWITCH_FALSE;
close_socket(&event_stream->socket);
switch_mutex_unlock(event_stream->socket_mutex);
switch_mutex_destroy(event_stream->socket_mutex);
/* clean up the memory */
switch_core_destroy_memory_pool(&event_stream->pool);
switch_atomic_dec(&globals.threads);
return NULL;
}
ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from) {
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
switch_memory_pool_t *pool = NULL;
ei_event_stream_t *event_stream;
/* create memory pool for this event stream */
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory: How many Alzheimer's patients does it take to screw in a light bulb? To get to the other side.\n");
return NULL;
}
/* from the memory pool, allocate the event stream structure */
if (!(event_stream = switch_core_alloc(pool, sizeof (*event_stream)))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory: I may have Alzheimers but at least I dont have Alzheimers.\n");
return NULL;
}
/* prepare the event stream */
memset(event_stream, 0, sizeof(*event_stream));
event_stream->bindings = NULL;
event_stream->pool = pool;
event_stream->connected = SWITCH_FALSE;
memcpy(&event_stream->pid, from, sizeof(erlang_pid));
switch_queue_create(&event_stream->queue, MAX_QUEUE_LEN, pool);
/* create a socket for accepting the event stream client */
if (!(event_stream->acceptor = create_socket(pool))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Like car accidents, most hardware problems are due to driver error.\n");
/* TODO: clean up */
return NULL;
}
if (switch_socket_opt_set(event_stream->acceptor, SWITCH_SO_NONBLOCK, TRUE)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Hey, it compiles!\n");
/* TODO: clean up */
return NULL;
}
/* create a pollset so we can efficiently check for new client connections */
if (switch_pollset_create(&event_stream->pollset, 1000, pool, 0) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "My software never has bugs. It just develops random features.\n");
/* TODO: clean up */
return NULL;
}
switch_socket_create_pollfd(&event_stream->pollfd, event_stream->acceptor, SWITCH_POLLIN | SWITCH_POLLERR, NULL, pool);
if (switch_pollset_add(event_stream->pollset, event_stream->pollfd) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "If you saw a heat wave, would you wave back?\n");
/* TODO: clean up */
return NULL;
}
switch_mutex_init(&event_stream->socket_mutex, SWITCH_MUTEX_DEFAULT, pool);
/* add the new event stream to the link list
* since the event streams link list is only
* accessed from the same thread no locks
* are required */
if (!*event_streams) {
*event_streams = event_stream;
} else {
event_stream->next = *event_streams;
*event_streams = event_stream;
}
/* when we start we are running */
switch_set_flag(event_stream, LFLAG_RUNNING);
switch_threadattr_create(&thd_attr, event_stream->pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, thd_attr, event_stream_loop, event_stream, event_stream->pool);
return event_stream;
}
unsigned long get_stream_port(const ei_event_stream_t *event_stream) {
switch_sockaddr_t *sa;
switch_socket_addr_get(&sa, SWITCH_FALSE, event_stream->acceptor);
return (unsigned long) switch_sockaddr_get_port(sa);
}
ei_event_stream_t *find_event_stream(ei_event_stream_t *event_stream, const erlang_pid *from) {
while (event_stream != NULL) {
if (ei_compare_pids(&event_stream->pid, from) == SWITCH_STATUS_SUCCESS) {
return event_stream;
}
event_stream = event_stream->next;
}
return NULL;
}
switch_status_t remove_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from) {
ei_event_stream_t *event_stream, *prev = NULL;
int found = 0;
/* if there are no event bindings there is nothing to do */
if (!*event_streams) {
return SWITCH_STATUS_SUCCESS;
}
/* try to find the event stream for the client process */
event_stream = *event_streams;
while(event_stream != NULL) {
if (ei_compare_pids(&event_stream->pid, from) == SWITCH_STATUS_SUCCESS) {
found = 1;
break;
}
prev = event_stream;
event_stream = event_stream->next;
}
if (found) {
/* if we found an event stream remove it from
* from the link list */
if (!prev) {
*event_streams = event_stream->next;
} else {
prev->next = event_stream->next;
}
/* stop the event stream thread */
switch_clear_flag(event_stream, LFLAG_RUNNING);
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t remove_event_streams(ei_event_stream_t **event_streams) {
ei_event_stream_t *event_stream = *event_streams;
while(event_stream != NULL) {
/* stop the event bindings publisher thread */
switch_clear_flag(event_stream, LFLAG_RUNNING);
event_stream = event_stream->next;
}
*event_streams = NULL;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name) {
ei_event_binding_t *event_binding = event_stream->bindings;
/* check if the event binding already exists, ignore if so */
while(event_binding != NULL) {
if (event_binding->type == SWITCH_EVENT_CUSTOM) {
if(subclass_name
&& event_binding->subclass_name
&& !strcmp(subclass_name, event_binding->subclass_name)) {
return SWITCH_STATUS_SUCCESS;
}
} else if (event_binding->type == event_type) {
return SWITCH_STATUS_SUCCESS;
}
event_binding = event_binding->next;
}
/* from the event stream memory pool, allocate the event binding structure */
if (!(event_binding = switch_core_alloc(event_stream->pool, sizeof (*event_binding)))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of random-access memory, attempting write-only memory\n");
return SWITCH_STATUS_FALSE;
}
/* prepare the event binding struct */
event_binding->type = event_type;
if (!subclass_name || zstr(subclass_name)) {
event_binding->subclass_name = NULL;
} else {
/* TODO: free strdup? */
event_binding->subclass_name = strdup(subclass_name);
}
event_binding->next = NULL;
/* bind to the event with a unique ID and capture the event_node pointer */
switch_uuid_str(event_binding->id, sizeof(event_binding->id));
if (switch_event_bind_removable(event_binding->id, event_type, subclass_name, event_handler, event_stream, &event_binding->node) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to bind to event %s %s!\n"
,switch_event_name(event_binding->type), event_binding->subclass_name ? event_binding->subclass_name : "");
return SWITCH_STATUS_GENERR;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding event binding %s to stream %p for %s <%d.%d.%d>: %s %s\n"
,event_binding->id, (void *)event_stream, event_stream->pid.node, event_stream->pid.creation
,event_stream->pid.num, event_stream->pid.serial, switch_event_name(event_binding->type)
,event_binding->subclass_name ? event_binding->subclass_name : "");
/* add the new binding to the list */
if (!event_stream->bindings) {
event_stream->bindings = event_binding;
} else {
event_binding->next = event_stream->bindings;
event_stream->bindings = event_binding;
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t remove_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name) {
ei_event_binding_t *event_binding = event_stream->bindings, *prev = NULL;
int found = 0;
/* if there are no bindings then there is nothing to do */
if (!event_binding) {
return SWITCH_STATUS_SUCCESS;
}
/* try to find the event binding specified */
while(event_binding != NULL) {
if (event_binding->type == SWITCH_EVENT_CUSTOM
&& subclass_name
&& event_binding->subclass_name
&& !strcmp(subclass_name, event_binding->subclass_name)) {
found = 1;
break;
} else if (event_binding->type == event_type) {
found = 1;
break;
}
prev = event_binding;
event_binding = event_binding->next;
}
if (found) {
/* if the event binding exists, unbind from the system */
switch_event_unbind(&event_binding->node);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing event binding %s from %p for %s <%d.%d.%d>: %s %s\n"
,event_binding->id, (void *)event_stream, event_stream->pid.node, event_stream->pid.creation
,event_stream->pid.num, event_stream->pid.serial, switch_event_name(event_binding->type)
,event_binding->subclass_name ? event_binding->subclass_name : "");
/* remove the event binding from the list */
if (!prev) {
event_stream->bindings = event_binding->next;
} else {
prev->next = event_binding->next;
}
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t remove_event_bindings(ei_event_stream_t *event_stream) {
ei_event_binding_t *event_binding = event_stream->bindings;
/* if there are no bindings then there is nothing to do */
if (!event_binding) {
return SWITCH_STATUS_SUCCESS;
}
/* try to find the event binding specified */
while(event_binding != NULL) {
/* if the event binding exists, unbind from the system */
switch_event_unbind(&event_binding->node);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing event binding %s from %p for %s <%d.%d.%d>: %s %s\n"
,event_binding->id, (void *)event_stream, event_stream->pid.node, event_stream->pid.creation
,event_stream->pid.num, event_stream->pid.serial, switch_event_name(event_binding->type)
,event_binding->subclass_name ? event_binding->subclass_name : "");
event_binding = event_binding->next;
}
event_stream->bindings = NULL;
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -0,0 +1,707 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Karl Anderson <karl@2600hz.com>
* Darren Schreiber <darren@2600hz.com>
*
*
* kazoo_fetch.c -- XML fetch request handler
*
*/
#include "mod_kazoo.h"
struct xml_fetch_reply_s {
char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
char *xml_str;
struct xml_fetch_reply_s *next;
};
typedef struct xml_fetch_reply_s xml_fetch_reply_t;
struct fetch_handler_s {
erlang_pid pid;
struct fetch_handler_s *next;
};
typedef struct fetch_handler_s fetch_handler_t;
struct ei_xml_client_s {
ei_node_t *ei_node;
fetch_handler_t *fetch_handlers;
struct ei_xml_client_s *next;
};
typedef struct ei_xml_client_s ei_xml_client_t;
struct ei_xml_agent_s {
switch_memory_pool_t *pool;
switch_xml_section_t section;
switch_thread_rwlock_t *lock;
ei_xml_client_t *clients;
switch_mutex_t *current_client_mutex;
ei_xml_client_t *current_client;
switch_mutex_t *replies_mutex;
switch_thread_cond_t *new_reply;
xml_fetch_reply_t *replies;
};
typedef struct ei_xml_agent_s ei_xml_agent_t;
static char *xml_section_to_string(switch_xml_section_t section) {
switch(section) {
case SWITCH_XML_SECTION_CONFIG:
return "configuration";
case SWITCH_XML_SECTION_DIRECTORY:
return "directory";
case SWITCH_XML_SECTION_DIALPLAN:
return "dialplan";
case SWITCH_XML_SECTION_CHATPLAN:
return "chatplan";
case SWITCH_XML_SECTION_CHANNELS:
return "channels";
default:
return "unknown";
}
}
static char *expand_vars(char *xml_str) {
char *var, *val;
char *rp = xml_str; /* read pointer */
char *ep, *wp, *buff; /* end pointer, write pointer, write buffer */
if (!(strstr(xml_str, "$${"))) {
return xml_str;
}
switch_zmalloc(buff, strlen(xml_str) * 2);
wp = buff;
ep = buff + (strlen(xml_str) * 2) - 1;
while (*rp && wp < ep) {
if (*rp == '$' && *(rp + 1) == '$' && *(rp + 2) == '{') {
char *e = switch_find_end_paren(rp + 2, '{', '}');
if (e) {
rp += 3;
var = rp;
*e++ = '\0';
rp = e;
if ((val = switch_core_get_variable_dup(var))) {
char *p;
for (p = val; p && *p && wp <= ep; p++) {
*wp++ = *p;
}
switch_safe_free(val);
}
continue;
}
}
*wp++ = *rp++;
}
*wp++ = '\0';
switch_safe_free(xml_str);
return buff;
}
static switch_xml_t fetch_handler(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params, void *user_data) {
switch_xml_t xml = NULL;
switch_uuid_t uuid;
switch_time_t now = 0;
ei_xml_agent_t *agent = (ei_xml_agent_t *) user_data;
ei_xml_client_t *client;
fetch_handler_t *fetch_handler;
xml_fetch_reply_t reply, *pending, *prev = NULL;
now = switch_micro_time_now();
if (!switch_test_flag(&globals, LFLAG_RUNNING)) {
return xml;
}
/* read-lock the agent */
switch_thread_rwlock_rdlock(agent->lock);
/* serialize access to current, used to round-robin requests */
/* TODO: check globals for round-robin boolean or loop all clients */
switch_mutex_lock(agent->current_client_mutex);
if (!agent->current_client) {
client = agent->clients;
} else {
client = agent->current_client;
}
if (client) {
agent->current_client = client->next;
}
switch_mutex_unlock(agent->current_client_mutex);
/* no client, no work required */
if (!client || !client->fetch_handlers) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "No %s XML erlang handler currently available\n"
,section);
switch_thread_rwlock_unlock(agent->lock);
return xml;
}
/* prepare the reply collector */
switch_uuid_get(&uuid);
switch_uuid_format(reply.uuid_str, &uuid);
reply.next = NULL;
reply.xml_str = NULL;
/* add our reply placeholder to the replies list */
switch_mutex_lock(agent->replies_mutex);
if (!agent->replies) {
agent->replies = &reply;
} else {
reply.next = agent->replies;
agent->replies = &reply;
}
switch_mutex_unlock(agent->replies_mutex);
fetch_handler = client->fetch_handlers;
while (fetch_handler != NULL) {
ei_send_msg_t *send_msg;
switch_malloc(send_msg, sizeof(*send_msg));
memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid));
ei_x_new_with_version(&send_msg->buf);
ei_x_encode_tuple_header(&send_msg->buf, 7);
ei_x_encode_atom(&send_msg->buf, "fetch");
ei_x_encode_atom(&send_msg->buf, section);
_ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined");
_ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined");
_ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined");
_ei_x_encode_string(&send_msg->buf, reply.uuid_str);
if (params) {
ei_encode_switch_event_headers(&send_msg->buf, params);
} else {
ei_x_encode_empty_list(&send_msg->buf);
}
if (switch_queue_trypush(client->ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send %s XML request to %s <%d.%d.%d>\n"
,section
,fetch_handler->pid.node
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
ei_x_free(&send_msg->buf);
switch_safe_free(send_msg);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending %s XML request (%s) to %s <%d.%d.%d>\n"
,section
,reply.uuid_str
,fetch_handler->pid.node
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
}
fetch_handler = fetch_handler->next;
}
/* wait for a reply (if there isnt already one...amazingly improbable but lets not take shortcuts */
switch_mutex_lock(agent->replies_mutex);
switch_thread_rwlock_unlock(agent->lock);
if (!reply.xml_str) {
switch_time_t timeout;
timeout = switch_micro_time_now() + 3000000;
while (switch_micro_time_now() < timeout) {
/* unlock the replies list and go to sleep, calculate a three second timeout before we started the loop
* plus 100ms to add a little hysteresis between the timeout and the while loop */
switch_thread_cond_timedwait(agent->new_reply, agent->replies_mutex, (timeout - switch_micro_time_now() + 100000));
/* if we woke up (and therefore have locked replies again) check if we got our reply
* otherwise we either timed-out (the while condition will fail) or one of
* our sibling processes got a reply and we should go back to sleep */
if (reply.xml_str) {
break;
}
}
}
/* find our reply placeholder in the linked list and remove it */
pending = agent->replies;
while (pending != NULL) {
if (pending->uuid_str == reply.uuid_str) {
break;
}
prev = pending;
pending = pending->next;
}
if (pending) {
if (!prev) {
agent->replies = reply.next;
} else {
prev->next = reply.next;
}
}
/* we are done with the replies link-list */
switch_mutex_unlock(agent->replies_mutex);
/* after all that did we get what we were after?! */
if (reply.xml_str) {
/* HELL YA WE DID */
reply.xml_str = expand_vars(reply.xml_str);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received %s XML (%s) after %dms: %s\n"
,section
,reply.uuid_str
,(unsigned int) (switch_micro_time_now() - now) / 1000
,reply.xml_str);
xml = switch_xml_parse_str_dynamic(reply.xml_str, SWITCH_FALSE);
} else {
/* facepalm */
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Request for %s XML (%s) timed-out after %dms\n"
,section
,reply.uuid_str
,(unsigned int) (switch_micro_time_now() - now) / 1000);
}
return xml;
}
static switch_status_t bind_fetch_agent(switch_xml_section_t section, switch_xml_binding_t **binding) {
switch_memory_pool_t *pool = NULL;
ei_xml_agent_t *agent;
/* create memory pool for this xml search binging (lives for duration of mod_kazoo runtime) */
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory: They're not people; they're hippies!\n");
return SWITCH_STATUS_MEMERR;
}
/* allocate some memory to store the fetch bindings for this section */
if (!(agent = switch_core_alloc(pool, sizeof (*agent)))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory: Oh, Jesus tap-dancing Christ!\n");
return SWITCH_STATUS_MEMERR;
}
/* try to bind to the switch */
if (switch_xml_bind_search_function_ret(fetch_handler, section, agent, binding) != SWITCH_STATUS_SUCCESS) {
switch_core_destroy_memory_pool(&pool);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not bind to FreeSWITCH %s XML requests\n"
,xml_section_to_string(section));
return SWITCH_STATUS_GENERR;
}
agent->pool = pool;
agent->section = section;
switch_thread_rwlock_create(&agent->lock, pool);
agent->clients = NULL;
switch_mutex_init(&agent->current_client_mutex, SWITCH_MUTEX_DEFAULT, pool);
agent->current_client = NULL;
switch_mutex_init(&agent->replies_mutex, SWITCH_MUTEX_DEFAULT, pool);
switch_thread_cond_create(&agent->new_reply, pool);
agent->replies = NULL;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Bound to %s XML requests\n"
,xml_section_to_string(section));
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t unbind_fetch_agent(switch_xml_binding_t **binding) {
ei_xml_agent_t *agent;
ei_xml_client_t *client;
/* get a pointer to our user_data */
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(*binding);
/* unbind from the switch */
switch_xml_unbind_search_function(binding);
/* LOCK ALL THE THINGS */
switch_thread_rwlock_wrlock(agent->lock);
switch_mutex_lock(agent->current_client_mutex);
switch_mutex_lock(agent->replies_mutex);
/* cleanly destroy each client */
client = agent->clients;
while(client != NULL) {
ei_xml_client_t *tmp_client = client;
fetch_handler_t *fetch_handler;
fetch_handler = client->fetch_handlers;
while(fetch_handler != NULL) {
fetch_handler_t *tmp_fetch_handler = fetch_handler;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removed %s XML handler %s <%d.%d.%d>\n"
,xml_section_to_string(agent->section)
,fetch_handler->pid.node
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
fetch_handler = fetch_handler->next;
switch_safe_free(tmp_fetch_handler);
}
client = client->next;
switch_safe_free(tmp_client);
}
/* keep the pointers clean, even if its just for a moment */
agent->clients = NULL;
agent->current_client = NULL;
/* release the locks! */
switch_thread_rwlock_unlock(agent->lock);
switch_mutex_unlock(agent->current_client_mutex);
switch_mutex_unlock(agent->replies_mutex);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unbound from %s XML requests\n"
,xml_section_to_string(agent->section));
/* cleanly destroy the bindings */
switch_thread_rwlock_destroy(agent->lock);
switch_mutex_destroy(agent->current_client_mutex);
switch_mutex_destroy(agent->replies_mutex);
switch_thread_cond_destroy(agent->new_reply);
switch_core_destroy_memory_pool(&agent->pool);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t remove_xml_client(ei_node_t *ei_node, switch_xml_binding_t *binding) {
ei_xml_agent_t *agent;
ei_xml_client_t *client, *prev = NULL;
int found = 0;
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
/* write-lock the agent */
switch_thread_rwlock_wrlock(agent->lock);
client = agent->clients;
while (client != NULL) {
if (client->ei_node == ei_node) {
found = 1;
break;
}
prev = client;
client = client->next;
}
if (found) {
fetch_handler_t *fetch_handler;
if (!prev) {
agent->clients = client->next;
} else {
prev->next = client->next;
}
/* the mutex lock is not required since we have the write lock
* but hey its fun and safe so do it anyway */
switch_mutex_lock(agent->current_client_mutex);
if (agent->current_client == client) {
agent->current_client = agent->clients;
}
switch_mutex_unlock(agent->current_client_mutex);
fetch_handler = client->fetch_handlers;
while(fetch_handler != NULL) {
fetch_handler_t *tmp_fetch_handler = fetch_handler;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removed %s XML handler %s <%d.%d.%d>\n"
,xml_section_to_string(agent->section)
,fetch_handler->pid.node
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
fetch_handler = fetch_handler->next;
switch_safe_free(tmp_fetch_handler);
}
switch_safe_free(client);
}
switch_thread_rwlock_unlock(agent->lock);
return SWITCH_STATUS_SUCCESS;
}
static ei_xml_client_t *add_xml_client(ei_node_t *ei_node, ei_xml_agent_t *agent) {
ei_xml_client_t *client;
switch_malloc(client, sizeof(*client));
client->ei_node = ei_node;
client->fetch_handlers = NULL;
client->next = NULL;
if (agent->clients) {
client->next = agent->clients;
}
agent->clients = client;
return client;
}
static ei_xml_client_t *find_xml_client(ei_node_t *ei_node, ei_xml_agent_t *agent) {
ei_xml_client_t *client;
client = agent->clients;
while (client != NULL) {
if (client->ei_node == ei_node) {
return client;
}
client = client->next;
}
return NULL;
}
static switch_status_t remove_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding) {
ei_xml_agent_t *agent;
ei_xml_client_t *client;
fetch_handler_t *fetch_handler, *prev = NULL;
int found = 0;
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
/* write-lock the agent */
switch_thread_rwlock_wrlock(agent->lock);
if (!(client = find_xml_client(ei_node, agent))) {
switch_thread_rwlock_unlock(agent->lock);
return SWITCH_STATUS_SUCCESS;
}
fetch_handler = client->fetch_handlers;
while (fetch_handler != NULL) {
if (ei_compare_pids(&fetch_handler->pid, from) == SWITCH_STATUS_SUCCESS) {
found = 1;
break;
}
prev = fetch_handler;
fetch_handler = fetch_handler->next;
}
if (found) {
if (!prev) {
client->fetch_handlers = fetch_handler->next;
} else {
prev->next = fetch_handler->next;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removed %s XML handler %s <%d.%d.%d>\n"
,xml_section_to_string(agent->section)
,fetch_handler->pid.node
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
switch_safe_free(fetch_handler);
}
switch_thread_rwlock_unlock(agent->lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_api_command_stream(ei_node_t *ei_node, switch_stream_handle_t *stream, switch_xml_binding_t *binding) {
ei_xml_agent_t *agent;
ei_xml_client_t *client;
if (!binding) {
return SWITCH_STATUS_GENERR;
}
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
/* read-lock the agent */
switch_thread_rwlock_rdlock(agent->lock);
client = agent->clients;
while (client != NULL) {
if (client->ei_node == ei_node) {
fetch_handler_t *fetch_handler;
fetch_handler = client->fetch_handlers;
while (fetch_handler != NULL) {
stream->write_function(stream, "XML %s handler <%d.%d.%d>\n"
,xml_section_to_string(agent->section)
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
fetch_handler = fetch_handler->next;
}
break;
}
client = client->next;
}
switch_thread_rwlock_unlock(agent->lock);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t bind_fetch_agents() {
bind_fetch_agent(SWITCH_XML_SECTION_CONFIG, &globals.config_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_DIRECTORY, &globals.directory_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_DIALPLAN, &globals.dialplan_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_CHATPLAN, &globals.chatplan_fetch_binding);
bind_fetch_agent(SWITCH_XML_SECTION_CHANNELS, &globals.channels_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t unbind_fetch_agents() {
unbind_fetch_agent(&globals.config_fetch_binding);
unbind_fetch_agent(&globals.directory_fetch_binding);
unbind_fetch_agent(&globals.dialplan_fetch_binding);
unbind_fetch_agent(&globals.chatplan_fetch_binding);
unbind_fetch_agent(&globals.channels_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t remove_xml_clients(ei_node_t *ei_node) {
remove_xml_client(ei_node, globals.config_fetch_binding);
remove_xml_client(ei_node, globals.directory_fetch_binding);
remove_xml_client(ei_node, globals.dialplan_fetch_binding);
remove_xml_client(ei_node, globals.chatplan_fetch_binding);
remove_xml_client(ei_node, globals.channels_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding) {
ei_xml_agent_t *agent;
ei_xml_client_t *client;
fetch_handler_t *fetch_handler;
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
/* write-lock the agent */
switch_thread_rwlock_wrlock(agent->lock);
if (!(client = find_xml_client(ei_node, agent))) {
client = add_xml_client(ei_node, agent);
}
fetch_handler = client->fetch_handlers;
while (fetch_handler != NULL) {
if (ei_compare_pids(&fetch_handler->pid, from) == SWITCH_STATUS_SUCCESS) {
switch_thread_rwlock_unlock(agent->lock);
return SWITCH_STATUS_SUCCESS;
}
fetch_handler = fetch_handler->next;
}
switch_malloc(fetch_handler, sizeof(*fetch_handler));
memcpy(&fetch_handler->pid, from, sizeof(erlang_pid));;
fetch_handler->next = NULL;
if (client->fetch_handlers) {
fetch_handler->next = client->fetch_handlers;
}
client->fetch_handlers = fetch_handler;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Added %s XML handler %s <%d.%d.%d>\n"
,xml_section_to_string(agent->section)
,fetch_handler->pid.node
,fetch_handler->pid.creation
,fetch_handler->pid.num
,fetch_handler->pid.serial);
switch_thread_rwlock_unlock(agent->lock);
ei_link(ei_node, ei_self(&globals.ei_cnode), from);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from) {
remove_fetch_handler(ei_node, from, globals.config_fetch_binding);
remove_fetch_handler(ei_node, from, globals.directory_fetch_binding);
remove_fetch_handler(ei_node, from, globals.dialplan_fetch_binding);
remove_fetch_handler(ei_node, from, globals.chatplan_fetch_binding);
remove_fetch_handler(ei_node, from, globals.channels_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t fetch_reply(char *uuid_str, char *xml_str, switch_xml_binding_t *binding) {
ei_xml_agent_t *agent;
xml_fetch_reply_t *reply;
switch_status_t status = SWITCH_STATUS_NOTFOUND;
agent = (ei_xml_agent_t *)switch_xml_get_binding_user_data(binding);
switch_mutex_lock(agent->replies_mutex);
reply = agent->replies;
while (reply != NULL) {
if (!strncmp(reply->uuid_str, uuid_str, SWITCH_UUID_FORMATTED_LENGTH)) {
if (!reply->xml_str) {
reply->xml_str = xml_str;
switch_thread_cond_broadcast(agent->new_reply);
status = SWITCH_STATUS_SUCCESS;
}
break;
}
reply = reply->next;
}
switch_mutex_unlock(agent->replies_mutex);
return status;
}
switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_handle_t *stream) {
handle_api_command_stream(ei_node, stream, globals.config_fetch_binding);
handle_api_command_stream(ei_node, stream, globals.directory_fetch_binding);
handle_api_command_stream(ei_node, stream, globals.dialplan_fetch_binding);
handle_api_command_stream(ei_node, stream, globals.chatplan_fetch_binding);
handle_api_command_stream(ei_node, stream, globals.channels_fetch_binding);
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,614 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
* Andrew Thompson <andrew@hijacked.us>
* Rob Charlton <rob.charlton@savageminds.com>
* Karl Anderson <karl@2600hz.com>
*
* Original from mod_erlang_event.
* ei_helpers.c -- helper functions for ei
*
*/
#include <switch.h>
#include <ei.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include "mod_kazoo.h"
/* Stolen from code added to ei in R12B-5.
* Since not everyone has this version yet;
* provide our own version.
* */
#define put8(s,n) do { \
(s)[0] = (char)((n) & 0xff); \
(s) += 1; \
} while (0)
#define put32be(s,n) do { \
(s)[0] = ((n) >> 24) & 0xff; \
(s)[1] = ((n) >> 16) & 0xff; \
(s)[2] = ((n) >> 8) & 0xff; \
(s)[3] = (n) & 0xff; \
(s) += 4; \
} while (0)
#ifdef EI_DEBUG
static void ei_x_print_reg_msg(ei_x_buff *buf, char *dest, int send) {
char *mbuf = NULL;
int i = 1;
ei_s_print_term(&mbuf, buf->buff, &i);
if (send) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Encoded term %s to '%s'\n", mbuf, dest);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Decoded term %s for '%s'\n", mbuf, dest);
}
free(mbuf);
}
static void ei_x_print_msg(ei_x_buff *buf, erlang_pid *pid, int send) {
char *pbuf = NULL;
int i = 0;
ei_x_buff pidbuf;
ei_x_new(&pidbuf);
ei_x_encode_pid(&pidbuf, pid);
ei_s_print_term(&pbuf, pidbuf.buff, &i);
ei_x_print_reg_msg(buf, pbuf, send);
free(pbuf);
}
#endif
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event) {
switch_event_header_t *hp;
char *uuid = switch_event_get_header(event, "unique-id");
int i;
for (i = 0, hp = event->headers; hp; hp = hp->next, i++);
if (event->body)
i++;
ei_x_encode_list_header(ebuf, i + 1);
if (uuid) {
char *unique_id = switch_event_get_header(event, "unique-id");
ei_x_encode_binary(ebuf, unique_id, strlen(unique_id));
} else {
ei_x_encode_atom(ebuf, "undefined");
}
for (hp = event->headers; hp; hp = hp->next) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_binary(ebuf, hp->name, strlen(hp->name));
switch_url_decode(hp->value);
ei_x_encode_binary(ebuf, hp->value, strlen(hp->value));
}
if (event->body) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_binary(ebuf, "body", strlen("body"));
ei_x_encode_binary(ebuf, event->body, strlen(event->body));
}
ei_x_encode_empty_list(ebuf);
}
void close_socket(switch_socket_t ** sock) {
if (*sock) {
switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE);
switch_socket_close(*sock);
*sock = NULL;
}
}
void close_socketfd(int *sockfd) {
if (*sockfd) {
shutdown(*sockfd, SHUT_RDWR);
close(*sockfd);
}
}
switch_socket_t *create_socket(switch_memory_pool_t *pool) {
switch_sockaddr_t *sa;
switch_socket_t *socket;
if(switch_sockaddr_info_get(&sa, globals.ip, SWITCH_UNSPEC, 0, 0, pool)) {
return NULL;
}
if (switch_socket_create(&socket, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool)) {
return NULL;
}
if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1)) {
return NULL;
}
if (switch_socket_bind(socket, sa)) {
return NULL;
}
if (switch_socket_listen(socket, 5)){
return NULL;
}
// if (globals.nat_map && switch_nat_get_type()) {
// switch_nat_add_mapping(port, SWITCH_NAT_TCP, NULL, SWITCH_FALSE);
// }
return socket;
}
switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode) {
struct hostent *nodehost;
char hostname[EI_MAXHOSTNAMELEN + 1] = "";
char nodename[MAXNODELEN + 1];
char cnodename[EI_MAXALIVELEN + 1];
//EI_MAX_COOKIE_SIZE+1
char *atsign;
/* copy the erlang interface nodename into something we can modify */
strncpy(cnodename, name, EI_MAXALIVELEN);
if ((atsign = strchr(cnodename, '@'))) {
/* we got a qualified node name, don't guess the host/domain */
snprintf(nodename, MAXNODELEN + 1, "%s", globals.ei_nodename);
/* truncate the alivename at the @ */
*atsign = '\0';
} else {
if ((nodehost = gethostbyaddr(ip_addr, sizeof (ip_addr), AF_INET))) {
memcpy(hostname, nodehost->h_name, EI_MAXHOSTNAMELEN);
}
if (zstr_buf(hostname) || !strncasecmp(globals.ip, "0.0.0.0", 7)) {
gethostname(hostname, EI_MAXHOSTNAMELEN);
}
snprintf(nodename, MAXNODELEN + 1, "%s@%s", globals.ei_nodename, hostname);
}
if (globals.ei_shortname) {
char *off;
if ((off = strchr(nodename, '.'))) {
*off = '\0';
}
}
/* init the ec stuff */
if (ei_connect_xinit(ei_cnode, hostname, cnodename, nodename, (Erl_IpAddr) ip_addr, globals.ei_cookie, 0) < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to initialize the erlang interface connection structure\n");
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
}
switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2) {
if ((!strcmp(pid1->node, pid2->node))
&& pid1->creation == pid2->creation
&& pid1->num == pid2->num
&& pid1->serial == pid2->serial) {
return SWITCH_STATUS_SUCCESS;
} else {
return SWITCH_STATUS_FALSE;
}
}
void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to) {
char msgbuf[2048];
char *s;
int index = 0;
index = 5; /* max sizes: */
ei_encode_version(msgbuf, &index); /* 1 */
ei_encode_tuple_header(msgbuf, &index, 3);
ei_encode_long(msgbuf, &index, ERL_LINK);
ei_encode_pid(msgbuf, &index, from); /* 268 */
ei_encode_pid(msgbuf, &index, to); /* 268 */
/* 5 byte header missing */
s = msgbuf;
put32be(s, index - 4); /* 4 */
put8(s, ERL_PASS_THROUGH); /* 1 */
/* sum: 542 */
if (write(ei_node->nodefd, msgbuf, index) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to link to process on %s\n", ei_node->peer_nodename);
}
}
void ei_encode_switch_event(ei_x_buff *ebuf, switch_event_t *event) {
ei_x_encode_tuple_header(ebuf, 2);
ei_x_encode_atom(ebuf, "event");
ei_encode_switch_event_headers(ebuf, event);
}
int ei_helper_send(ei_node_t *ei_node, erlang_pid *to, ei_x_buff *buf) {
int ret = 0;
if (ei_node->nodefd) {
#ifdef EI_DEBUG
ei_x_print_msg(buf, to, 1);
#endif
ret = ei_send(ei_node->nodefd, to, buf->buff, buf->index);
}
return ret;
}
int ei_decode_atom_safe(char *buf, int *index, char *dst) {
int type, size;
ei_get_type(buf, index, &type, &size);
if (type != ERL_ATOM_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed atom\n", type, size);
return -1;
} else if (size > MAXATOMLEN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of atom with size %d into a buffer of size %d\n", size, MAXATOMLEN);
return -1;
} else {
return ei_decode_atom(buf, index, dst);
}
}
int ei_decode_string_or_binary(char *buf, int *index, char **dst) {
int type, size, res;
long len;
ei_get_type(buf, index, &type, &size);
if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size);
return -1;
}
*dst = malloc(size + 1);
if (type == ERL_NIL_EXT) {
res = 0;
**dst = '\0';
} else if (type == ERL_BINARY_EXT) {
res = ei_decode_binary(buf, index, *dst, &len);
(*dst)[len] = '\0';
} else {
res = ei_decode_string(buf, index, *dst);
}
return res;
}
int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst) {
int type, size, res;
long len;
ei_get_type(buf, index, &type, &size);
if (type != ERL_STRING_EXT && type != ERL_BINARY_EXT && type != ERL_NIL_EXT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang term type %d (size %d), needed binary or string\n", type, size);
return -1;
}
if (size > maxsize) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Requested decoding of %s with size %d into a buffer of size %d\n",
type == ERL_BINARY_EXT ? "binary" : "string", size, maxsize);
return -1;
}
if (type == ERL_NIL_EXT) {
res = 0;
dst = '\0';
} else if (type == ERL_BINARY_EXT) {
res = ei_decode_binary(buf, index, dst, &len);
dst[len] = '\0'; /* binaries aren't null terminated */
} else {
res = ei_decode_string(buf, index, dst);
}
return res;
}
switch_hash_t *create_default_filter() {
switch_hash_t *filter;
switch_core_hash_init(&filter);
switch_core_hash_insert(filter, "Acquired-UUID", "1");
switch_core_hash_insert(filter, "action", "1");
switch_core_hash_insert(filter, "Action", "1");
switch_core_hash_insert(filter, "alt_event_type", "1");
switch_core_hash_insert(filter, "Answer-State", "1");
switch_core_hash_insert(filter, "Application", "1");
switch_core_hash_insert(filter, "Application-Data", "1");
switch_core_hash_insert(filter, "Application-Name", "1");
switch_core_hash_insert(filter, "Application-Response", "1");
switch_core_hash_insert(filter, "att_xfer_replaced_by", "1");
switch_core_hash_insert(filter, "Auth-Method", "1");
switch_core_hash_insert(filter, "Auth-Realm", "1");
switch_core_hash_insert(filter, "Auth-User", "1");
switch_core_hash_insert(filter, "Bridge-A-Unique-ID", "1");
switch_core_hash_insert(filter, "Bridge-B-Unique-ID", "1");
switch_core_hash_insert(filter, "Call-Direction", "1");
switch_core_hash_insert(filter, "Caller-Callee-ID-Name", "1");
switch_core_hash_insert(filter, "Caller-Callee-ID-Number", "1");
switch_core_hash_insert(filter, "Caller-Caller-ID-Name", "1");
switch_core_hash_insert(filter, "Caller-Caller-ID-Number", "1");
switch_core_hash_insert(filter, "Caller-Context", "1");
switch_core_hash_insert(filter, "Caller-Controls", "1");
switch_core_hash_insert(filter, "Caller-Destination-Number", "1");
switch_core_hash_insert(filter, "Caller-Dialplan", "1");
switch_core_hash_insert(filter, "Caller-Network-Addr", "1");
switch_core_hash_insert(filter, "Caller-Unique-ID", "1");
switch_core_hash_insert(filter, "Call-ID", "1");
switch_core_hash_insert(filter, "Channel-Call-State", "1");
switch_core_hash_insert(filter, "Channel-Call-UUID", "1");
switch_core_hash_insert(filter, "Channel-Presence-ID", "1");
switch_core_hash_insert(filter, "Channel-State", "1");
switch_core_hash_insert(filter, "Chat-Permissions", "1");
switch_core_hash_insert(filter, "Conference-Name", "1");
switch_core_hash_insert(filter, "Conference-Profile-Name", "1");
switch_core_hash_insert(filter, "Conference-Unique-ID", "1");
switch_core_hash_insert(filter, "contact", "1");
switch_core_hash_insert(filter, "Detected-Tone", "1");
switch_core_hash_insert(filter, "dialog_state", "1");
switch_core_hash_insert(filter, "direction", "1");
switch_core_hash_insert(filter, "Distributed-From", "1");
switch_core_hash_insert(filter, "DTMF-Digit", "1");
switch_core_hash_insert(filter, "DTMF-Duration", "1");
switch_core_hash_insert(filter, "Event-Date-Timestamp", "1");
switch_core_hash_insert(filter, "Event-Name", "1");
switch_core_hash_insert(filter, "Event-Subclass", "1");
switch_core_hash_insert(filter, "expires", "1");
switch_core_hash_insert(filter, "Expires", "1");
switch_core_hash_insert(filter, "Ext-SIP-IP", "1");
switch_core_hash_insert(filter, "File", "1");
switch_core_hash_insert(filter, "FreeSWITCH-Hostname", "1");
switch_core_hash_insert(filter, "from", "1");
switch_core_hash_insert(filter, "Hunt-Destination-Number", "1");
switch_core_hash_insert(filter, "ip", "1");
switch_core_hash_insert(filter, "Message-Account", "1");
switch_core_hash_insert(filter, "metadata", "1");
switch_core_hash_insert(filter, "old_node_channel_uuid", "1");
switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Name", "1");
switch_core_hash_insert(filter, "Other-Leg-Callee-ID-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Name", "1");
switch_core_hash_insert(filter, "Other-Leg-Caller-ID-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Destination-Number", "1");
switch_core_hash_insert(filter, "Other-Leg-Direction", "1");
switch_core_hash_insert(filter, "Other-Leg-Unique-ID", "1");
switch_core_hash_insert(filter, "Other-Leg-Channel-Name", "1");
switch_core_hash_insert(filter, "Participant-Type", "1");
switch_core_hash_insert(filter, "Path", "1");
switch_core_hash_insert(filter, "profile_name", "1");
switch_core_hash_insert(filter, "Profiles", "1");
switch_core_hash_insert(filter, "proto-specific-event-name", "1");
switch_core_hash_insert(filter, "Raw-Application-Data", "1");
switch_core_hash_insert(filter, "realm", "1");
switch_core_hash_insert(filter, "Resigning-UUID", "1");
switch_core_hash_insert(filter, "set", "1");
switch_core_hash_insert(filter, "sip_auto_answer", "1");
switch_core_hash_insert(filter, "sip_auth_method", "1");
switch_core_hash_insert(filter, "sip_from_host", "1");
switch_core_hash_insert(filter, "sip_from_user", "1");
switch_core_hash_insert(filter, "sip_to_host", "1");
switch_core_hash_insert(filter, "sip_to_user", "1");
switch_core_hash_insert(filter, "sub-call-id", "1");
switch_core_hash_insert(filter, "technology", "1");
switch_core_hash_insert(filter, "to", "1");
switch_core_hash_insert(filter, "Unique-ID", "1");
switch_core_hash_insert(filter, "URL", "1");
switch_core_hash_insert(filter, "username", "1");
switch_core_hash_insert(filter, "variable_channel_is_moving", "1");
switch_core_hash_insert(filter, "variable_collected_digits", "1");
switch_core_hash_insert(filter, "variable_current_application", "1");
switch_core_hash_insert(filter, "variable_current_application_data", "1");
switch_core_hash_insert(filter, "variable_domain_name", "1");
switch_core_hash_insert(filter, "variable_effective_caller_id_name", "1");
switch_core_hash_insert(filter, "variable_effective_caller_id_number", "1");
switch_core_hash_insert(filter, "variable_holding_uuid", "1");
switch_core_hash_insert(filter, "variable_hold_music", "1");
switch_core_hash_insert(filter, "variable_media_group_id", "1");
switch_core_hash_insert(filter, "variable_originate_disposition", "1");
switch_core_hash_insert(filter, "variable_origination_uuid", "1");
switch_core_hash_insert(filter, "variable_playback_terminator_used", "1");
switch_core_hash_insert(filter, "variable_presence_id", "1");
switch_core_hash_insert(filter, "variable_record_ms", "1");
switch_core_hash_insert(filter, "variable_recovered", "1");
switch_core_hash_insert(filter, "variable_silence_hits_exhausted", "1");
switch_core_hash_insert(filter, "variable_sip_auth_realm", "1");
switch_core_hash_insert(filter, "variable_sip_from_host", "1");
switch_core_hash_insert(filter, "variable_sip_from_user", "1");
switch_core_hash_insert(filter, "variable_sip_from_tag", "1");
switch_core_hash_insert(filter, "variable_sip_h_X-AUTH-IP", "1");
switch_core_hash_insert(filter, "variable_sip_received_ip", "1");
switch_core_hash_insert(filter, "variable_sip_to_host", "1");
switch_core_hash_insert(filter, "variable_sip_to_user", "1");
switch_core_hash_insert(filter, "variable_sip_to_tag", "1");
switch_core_hash_insert(filter, "variable_sofia_profile_name", "1");
switch_core_hash_insert(filter, "variable_transfer_history", "1");
switch_core_hash_insert(filter, "variable_user_name", "1");
switch_core_hash_insert(filter, "variable_endpoint_disposition", "1");
switch_core_hash_insert(filter, "variable_originate_disposition", "1");
switch_core_hash_insert(filter, "variable_bridge_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_last_bridge_proto_specific_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_proto_specific_hangup_cause", "1");
switch_core_hash_insert(filter, "VM-Call-ID", "1");
switch_core_hash_insert(filter, "VM-sub-call-id", "1");
switch_core_hash_insert(filter, "whistle_application_name", "1");
switch_core_hash_insert(filter, "whistle_application_response", "1");
switch_core_hash_insert(filter, "whistle_event_name", "1");
switch_core_hash_insert(filter, "sip_auto_answer_notify", "1");
switch_core_hash_insert(filter, "eavesdrop_group", "1");
switch_core_hash_insert(filter, "origination_caller_id_name", "1");
switch_core_hash_insert(filter, "origination_caller_id_number", "1");
switch_core_hash_insert(filter, "origination_callee_id_name", "1");
switch_core_hash_insert(filter, "origination_callee_id_number", "1");
switch_core_hash_insert(filter, "sip_auth_username", "1");
switch_core_hash_insert(filter, "sip_auth_password", "1");
switch_core_hash_insert(filter, "effective_caller_id_name", "1");
switch_core_hash_insert(filter, "effective_caller_id_number", "1");
switch_core_hash_insert(filter, "effective_callee_id_name", "1");
switch_core_hash_insert(filter, "effective_callee_id_number", "1");
/* Registration headers */
switch_core_hash_insert(filter, "call-id", "1");
switch_core_hash_insert(filter, "profile-name", "1");
switch_core_hash_insert(filter, "from-user", "1");
switch_core_hash_insert(filter, "from-host", "1");
switch_core_hash_insert(filter, "presence-hosts", "1");
switch_core_hash_insert(filter, "contact", "1");
switch_core_hash_insert(filter, "rpid", "1");
switch_core_hash_insert(filter, "status", "1");
switch_core_hash_insert(filter, "expires", "1");
switch_core_hash_insert(filter, "to-user", "1");
switch_core_hash_insert(filter, "to-host", "1");
switch_core_hash_insert(filter, "network-ip", "1");
switch_core_hash_insert(filter, "network-port", "1");
switch_core_hash_insert(filter, "username", "1");
switch_core_hash_insert(filter, "realm", "1");
switch_core_hash_insert(filter, "user-agent", "1");
switch_core_hash_insert(filter, "Hangup-Cause", "1");
switch_core_hash_insert(filter, "Unique-ID", "1");
switch_core_hash_insert(filter, "variable_switch_r_sdp", "1");
switch_core_hash_insert(filter, "variable_sip_local_sdp_str", "1");
switch_core_hash_insert(filter, "variable_sip_to_uri", "1");
switch_core_hash_insert(filter, "variable_sip_from_uri", "1");
switch_core_hash_insert(filter, "variable_sip_user_agent", "1");
switch_core_hash_insert(filter, "variable_duration", "1");
switch_core_hash_insert(filter, "variable_billsec", "1");
switch_core_hash_insert(filter, "variable_progresssec", "1");
switch_core_hash_insert(filter, "variable_progress_uepoch", "1");
switch_core_hash_insert(filter, "variable_progress_media_uepoch", "1");
switch_core_hash_insert(filter, "variable_start_uepoch", "1");
switch_core_hash_insert(filter, "variable_digits_dialed", "1");
switch_core_hash_insert(filter, "Member-ID", "1");
switch_core_hash_insert(filter, "Floor", "1");
switch_core_hash_insert(filter, "Video", "1");
switch_core_hash_insert(filter, "Hear", "1");
switch_core_hash_insert(filter, "Speak", "1");
switch_core_hash_insert(filter, "Talking", "1");
switch_core_hash_insert(filter, "Current-Energy", "1");
switch_core_hash_insert(filter, "Energy-Level", "1");
switch_core_hash_insert(filter, "Mute-Detect", "1");
/* RTMP headers */
switch_core_hash_insert(filter, "RTMP-Session-ID", "1");
switch_core_hash_insert(filter, "RTMP-Profile", "1");
switch_core_hash_insert(filter, "RTMP-Flash-Version", "1");
switch_core_hash_insert(filter, "RTMP-SWF-URL", "1");
switch_core_hash_insert(filter, "RTMP-TC-URL", "1");
switch_core_hash_insert(filter, "RTMP-Page-URL", "1");
switch_core_hash_insert(filter, "User", "1");
switch_core_hash_insert(filter, "Domain", "1");
/* Fax headers */
switch_core_hash_insert(filter, "variable_fax_bad_rows", "1");
switch_core_hash_insert(filter, "variable_fax_document_total_pages", "1");
switch_core_hash_insert(filter, "variable_fax_document_transferred_pages", "1");
switch_core_hash_insert(filter, "variable_fax_ecm_used", "1");
switch_core_hash_insert(filter, "variable_fax_result_code", "1");
switch_core_hash_insert(filter, "variable_fax_result_text", "1");
switch_core_hash_insert(filter, "variable_fax_success", "1");
switch_core_hash_insert(filter, "variable_fax_transfer_rate", "1");
switch_core_hash_insert(filter, "variable_fax_local_station_id", "1");
switch_core_hash_insert(filter, "variable_fax_remote_station_id", "1");
switch_core_hash_insert(filter, "variable_fax_remote_country", "1");
switch_core_hash_insert(filter, "variable_fax_remote_vendor", "1");
switch_core_hash_insert(filter, "variable_fax_remote_model", "1");
switch_core_hash_insert(filter, "variable_fax_image_resolution", "1");
switch_core_hash_insert(filter, "variable_fax_file_image_resolution", "1");
switch_core_hash_insert(filter, "variable_fax_image_size", "1");
switch_core_hash_insert(filter, "variable_fax_image_pixel_size", "1");
switch_core_hash_insert(filter, "variable_fax_file_image_pixel_size", "1");
switch_core_hash_insert(filter, "variable_fax_longest_bad_row_run", "1");
switch_core_hash_insert(filter, "variable_fax_encoding", "1");
switch_core_hash_insert(filter, "variable_fax_encoding_name", "1");
switch_core_hash_insert(filter, "variable_fax_header", "1");
switch_core_hash_insert(filter, "variable_fax_ident", "1");
switch_core_hash_insert(filter, "variable_fax_timezone", "1");
switch_core_hash_insert(filter, "variable_fax_doc_id", "1");
switch_core_hash_insert(filter, "variable_fax_doc_database", "1");
/* Secure headers */
/*
switch_core_hash_insert(filter, "variable_sdp_secure_savp_only", "1");
switch_core_hash_insert(filter, "variable_rtp_has_crypto", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "variable_rtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "variable_zrtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "sdp_secure_savp_only", "1");
switch_core_hash_insert(filter, "rtp_has_crypto", "1");
switch_core_hash_insert(filter, "rtp_secure_media", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "rtp_secure_media_confirmed_video", "1");
switch_core_hash_insert(filter, "zrtp_secure_media", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_audio", "1");
switch_core_hash_insert(filter, "zrtp_secure_media_confirmed_video", "1");
*/
/* Device Redirect headers */
/*
switch_core_hash_insert(filter, "variable_last_bridge_hangup_cause", "1");
switch_core_hash_insert(filter, "variable_sip_redirected_by", "1");
*/
switch_core_hash_insert(filter, "intercepted_by", "1");
// SMS
switch_core_hash_insert(filter, "Message-ID", "1");
switch_core_hash_insert(filter, "Delivery-Failure", "1");
switch_core_hash_insert(filter, "Delivery-Result-Code", "1");
return filter;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -0,0 +1,776 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Karl Anderson <karl@2600hz.com>
* Darren Schreiber <darren@2600hz.com>
*
*
* mod_kazoo.c -- Socket Controlled Event Handler
*
*/
#include "mod_kazoo.h"
#define KAZOO_DESC "kazoo information"
#define KAZOO_SYNTAX "<command> [<args>]"
SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown);
SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime);
SWITCH_MODULE_DEFINITION(mod_kazoo, mod_kazoo_load, mod_kazoo_shutdown, mod_kazoo_runtime);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ip, globals.ip);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_cookie, globals.ei_cookie);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_ei_nodename, globals.ei_nodename);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_pref_kazoo_var_prefix, globals.kazoo_var_prefix);
static switch_status_t api_erlang_status(switch_stream_handle_t *stream) {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[25];
const char *ip_addr;
ei_node_t *ei_node;
switch_socket_addr_get(&sa, SWITCH_FALSE, globals.acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
stream->write_function(stream, "Running %s\n", VERSION);
stream->write_function(stream, "Listening for new Erlang connections on %s:%u with cookie %s\n", ip_addr, port, globals.ei_cookie);
stream->write_function(stream, "Registered as Erlang node %s, visible as %s\n", globals.ei_cnode.thisnodename, globals.ei_cnode.thisalivename);
if (globals.ei_compat_rel) {
stream->write_function(stream, "Using Erlang compatibility mode: %d\n", globals.ei_compat_rel);
}
switch_thread_rwlock_rdlock(globals.ei_nodes_lock);
ei_node = globals.ei_nodes;
if (!ei_node) {
stream->write_function(stream, "No erlang nodes connected\n");
} else {
stream->write_function(stream, "Connected to:\n");
while(ei_node != NULL) {
unsigned int year, day, hour, min, sec, delta;
delta = (switch_micro_time_now() - ei_node->created_time) / 1000000;
sec = delta % 60;
min = delta / 60 % 60;
hour = delta / 3600 % 24;
day = delta / 86400 % 7;
year = delta / 31556926 % 12;
stream->write_function(stream, " %s (%s:%d) up %d years, %d days, %d hours, %d minutes, %d seconds\n"
,ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, year, day, hour, min, sec);
ei_node = ei_node->next;
}
}
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_event_filter(switch_stream_handle_t *stream) {
switch_hash_index_t *hi = NULL;
int column = 0;
for (hi = (switch_hash_index_t *)switch_core_hash_first_iter(globals.event_filter, hi); hi; hi = switch_core_hash_next(&hi)) {
const void *key;
void *val;
switch_core_hash_this(hi, &key, NULL, &val);
stream->write_function(stream, "%-50s", (char *)key);
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
}
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
stream->write_function(stream, "%-50s", globals.kazoo_var_prefix);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_nodes_list(switch_stream_handle_t *stream) {
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(globals.ei_nodes_lock);
ei_node = globals.ei_nodes;
while(ei_node != NULL) {
stream->write_function(stream, "%s (%s)\n", ei_node->peer_nodename, ei_node->remote_ip);
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_nodes_count(switch_stream_handle_t *stream) {
ei_node_t *ei_node;
int count = 0;
switch_thread_rwlock_rdlock(globals.ei_nodes_lock);
ei_node = globals.ei_nodes;
while(ei_node != NULL) {
count++;
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
stream->write_function(stream, "%d\n", count);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_complete_erlang_node(const char *line, const char *cursor, switch_console_callback_match_t **matches) {
switch_console_callback_match_t *my_matches = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(globals.ei_nodes_lock);
ei_node = globals.ei_nodes;
while(ei_node != NULL) {
switch_console_push_match(&my_matches, ei_node->peer_nodename);
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
if (my_matches) {
*matches = my_matches;
status = SWITCH_STATUS_SUCCESS;
}
return status;
}
static switch_status_t handle_node_api_event_stream(ei_event_stream_t *event_stream, switch_stream_handle_t *stream) {
ei_event_binding_t *binding;
int column = 0;
switch_mutex_lock(event_stream->socket_mutex);
if (event_stream->connected == SWITCH_FALSE) {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[25] = {0};
const char *ip_addr;
switch_socket_addr_get(&sa, SWITCH_TRUE, event_stream->acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
if (zstr(ip_addr)) {
ip_addr = globals.ip;
}
stream->write_function(stream, "%s:%d -> disconnected\n"
,ip_addr, port);
} else {
stream->write_function(stream, "%s:%d -> %s:%d\n"
,event_stream->local_ip, event_stream->local_port
,event_stream->remote_ip, event_stream->remote_port);
}
binding = event_stream->bindings;
while(binding != NULL) {
if (binding->type == SWITCH_EVENT_CUSTOM) {
stream->write_function(stream, "CUSTOM %-43s", binding->subclass_name);
} else {
stream->write_function(stream, "%-50s", switch_event_name(binding->type));
}
if (++column > 2) {
stream->write_function(stream, "\n");
column = 0;
}
binding = binding->next;
}
switch_mutex_unlock(event_stream->socket_mutex);
if (!column) {
stream->write_function(stream, "\n");
} else {
stream->write_function(stream, "\n\n");
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_node_api_event_streams(ei_node_t *ei_node, switch_stream_handle_t *stream) {
ei_event_stream_t *event_stream;
switch_mutex_lock(ei_node->event_streams_mutex);
event_stream = ei_node->event_streams;
while(event_stream != NULL) {
handle_node_api_event_stream(event_stream, stream);
event_stream = event_stream->next;
}
switch_mutex_unlock(ei_node->event_streams_mutex);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t handle_node_api_command(ei_node_t *ei_node, switch_stream_handle_t *stream, uint32_t command) {
unsigned int year, day, hour, min, sec, delta;
switch (command) {
case API_COMMAND_DISCONNECT:
stream->write_function(stream, "Disconnecting erlang node %s at managers request\n", ei_node->peer_nodename);
switch_clear_flag(ei_node, LFLAG_RUNNING);
break;
case API_COMMAND_REMOTE_IP:
delta = (switch_micro_time_now() - ei_node->created_time) / 1000000;
sec = delta % 60;
min = delta / 60 % 60;
hour = delta / 3600 % 24;
day = delta / 86400 % 7;
year = delta / 31556926 % 12;
stream->write_function(stream, "Uptime %d years, %d days, %d hours, %d minutes, %d seconds\n", year, day, hour, min, sec);
stream->write_function(stream, "Local Address %s:%d\n", ei_node->local_ip, ei_node->local_port);
stream->write_function(stream, "Remote Address %s:%d\n", ei_node->remote_ip, ei_node->remote_port);
break;
case API_COMMAND_STREAMS:
handle_node_api_event_streams(ei_node, stream);
break;
case API_COMMAND_BINDINGS:
handle_api_command_streams(ei_node, stream);
break;
default:
break;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t api_erlang_node_command(switch_stream_handle_t *stream, const char *nodename, uint32_t command) {
ei_node_t *ei_node;
switch_thread_rwlock_rdlock(globals.ei_nodes_lock);
ei_node = globals.ei_nodes;
while(ei_node != NULL) {
int length = strlen(ei_node->peer_nodename);
if (!strncmp(ei_node->peer_nodename, nodename, length)) {
handle_node_api_command(ei_node, stream, command);
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
return SWITCH_STATUS_SUCCESS;
}
ei_node = ei_node->next;
}
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
return SWITCH_STATUS_NOTFOUND;
}
static int read_cookie_from_file(char *filename) {
int fd;
char cookie[MAXATOMLEN + 1];
char *end;
struct stat buf;
ssize_t res;
if (!stat(filename, &buf)) {
if ((buf.st_mode & S_IRWXG) || (buf.st_mode & S_IRWXO)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s must only be accessible by owner only.\n", filename);
return 2;
}
if (buf.st_size > MAXATOMLEN) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s contains a cookie larger than the maximum atom size of %d.\n", filename, MAXATOMLEN);
return 2;
}
fd = open(filename, O_RDONLY);
if (fd < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open cookie file %s : %d.\n", filename, errno);
return 2;
}
if ((res = read(fd, cookie, MAXATOMLEN)) < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie file %s : %d.\n", filename, errno);
}
cookie[MAXATOMLEN] = '\0';
/* replace any end of line characters with a null */
if ((end = strchr(cookie, '\n'))) {
*end = '\0';
}
if ((end = strchr(cookie, '\r'))) {
*end = '\0';
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie from file %s: %s\n", filename, cookie);
set_pref_ei_cookie(cookie);
return 0;
} else {
/* don't error here, because we might be blindly trying to read $HOME/.erlang.cookie, and that can fail silently */
return 1;
}
}
static switch_status_t config(void) {
char *cf = "kazoo.conf";
switch_xml_t cfg, xml, child, param;
globals.send_all_headers = globals.send_all_private_headers = 0;
globals.connection_timeout = 500;
globals.receive_timeout = 200;
globals.receive_msg_preallocate = 2000;
globals.event_stream_preallocate = 4000;
globals.send_msg_batch = 10;
globals.event_stream_framing = 2;
if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open configuration file %s\n", cf);
return SWITCH_STATUS_FALSE;
} else {
if ((child = switch_xml_child(cfg, "settings"))) {
for (param = switch_xml_child(child, "param"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
char *val = (char *) switch_xml_attr_soft(param, "value");
if (!strcmp(var, "listen-ip")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set bind ip address: %s\n", val);
set_pref_ip(val);
} else if (!strcmp(var, "cookie")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set cookie: %s\n", val);
set_pref_ei_cookie(val);
} else if (!strcmp(var, "cookie-file")) {
if (read_cookie_from_file(val) == 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to read cookie from %s\n", val);
}
} else if (!strcmp(var, "nodename")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set node name: %s\n", val);
set_pref_ei_nodename(val);
} else if (!strcmp(var, "shortname")) {
globals.ei_shortname = switch_true(val);
} else if (!strcmp(var, "kazoo-var-prefix")) {
set_pref_kazoo_var_prefix(val);
} else if (!strcmp(var, "compat-rel")) {
if (atoi(val) >= 7)
globals.ei_compat_rel = atoi(val);
else
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid compatibility release '%s' specified\n", val);
} else if (!strcmp(var, "nat-map")) {
globals.nat_map = switch_true(val);
} else if (!strcmp(var, "send-all-headers")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-headers: %s\n", val);
globals.send_all_headers = switch_true(val);
} else if (!strcmp(var, "send-all-private-headers")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-all-private-headers: %s\n", val);
globals.send_all_private_headers = switch_true(val);
} else if (!strcmp(var, "connection-timeout")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set connection-timeout: %s\n", val);
globals.connection_timeout = atoi(val);
} else if (!strcmp(var, "receive-timeout")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-timeout: %s\n", val);
globals.receive_timeout = atoi(val);
} else if (!strcmp(var, "receive-msg-preallocate")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set receive-msg-preallocate: %s\n", val);
globals.receive_msg_preallocate = atoi(val);
} else if (!strcmp(var, "event-stream-preallocate")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-preallocate: %s\n", val);
globals.event_stream_preallocate = atoi(val);
} else if (!strcmp(var, "send-msg-batch-size")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set send-msg-batch-size: %s\n", val);
globals.send_msg_batch = atoi(val);
} else if (!strcmp(var, "event-stream-framing")) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set event-stream-framing: %s\n", val);
globals.event_stream_framing = atoi(val);
}
}
}
if ((child = switch_xml_child(cfg, "event-filter"))) {
switch_hash_t *filter;
switch_core_hash_init(&filter);
for (param = switch_xml_child(child, "header"); param; param = param->next) {
char *var = (char *) switch_xml_attr_soft(param, "name");
switch_core_hash_insert(filter, var, "1");
}
globals.event_filter = filter;
}
switch_xml_free(xml);
}
if (globals.receive_msg_preallocate < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid receive message preallocate value, disabled\n");
globals.receive_msg_preallocate = 0;
}
if (globals.event_stream_preallocate < 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream preallocate value, disabled\n");
globals.event_stream_preallocate = 0;
}
if (globals.send_msg_batch < 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid send message batch size, reverting to default\n");
globals.send_msg_batch = 10;
}
if (!globals.event_filter) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Event filter not found in configuration, using default\n");
globals.event_filter = create_default_filter();
}
if (globals.event_stream_framing < 1 || globals.event_stream_framing > 4) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid event stream framing value, using default\n");
globals.event_stream_framing = 2;
}
if (zstr(globals.kazoo_var_prefix)) {
set_pref_kazoo_var_prefix("variable_ecallmgr*");
globals.var_prefix_length = 17; //ignore the *
} else {
/* we could use the global pool but then we would have to conditionally
* free the pointer if it was not drawn from the XML */
char *buf;
int size = switch_snprintf(NULL, 0, "variable_%s*", globals.kazoo_var_prefix) + 1;
switch_malloc(buf, size);
switch_snprintf(buf, size, "variable_%s*", globals.kazoo_var_prefix);
switch_safe_free(globals.kazoo_var_prefix);
globals.kazoo_var_prefix = buf;
globals.var_prefix_length = size - 2; //ignore the *
}
if (!globals.num_worker_threads) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Number of worker threads not found in configuration, using default\n");
globals.num_worker_threads = 10;
}
if (zstr(globals.ip)) {
set_pref_ip("0.0.0.0");
}
if (zstr(globals.ei_cookie)) {
int res;
char *home_dir = getenv("HOME");
char path_buf[1024];
if (!zstr(home_dir)) {
/* $HOME/.erlang.cookie */
switch_snprintf(path_buf, sizeof (path_buf), "%s%s%s", home_dir, SWITCH_PATH_SEPARATOR, ".erlang.cookie");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking for cookie at path: %s\n", path_buf);
res = read_cookie_from_file(path_buf);
if (res) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No cookie or valid cookie file specified, using default cookie\n");
set_pref_ei_cookie("ClueCon");
}
}
}
if (!globals.ei_nodename) {
set_pref_ei_nodename("freeswitch");
}
if (!globals.nat_map) {
globals.nat_map = 0;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t create_acceptor() {
switch_sockaddr_t *sa;
uint16_t port;
char ipbuf[25];
const char *ip_addr;
/* if the config has specified an erlang release compatibility then pass that along to the erlang interface */
if (globals.ei_compat_rel) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Compatability with OTP R%d requested\n", globals.ei_compat_rel);
ei_set_compat_rel(globals.ei_compat_rel);
}
if (!(globals.acceptor = create_socket(globals.pool))) {
return SWITCH_STATUS_SOCKERR;
}
switch_socket_addr_get(&sa, SWITCH_FALSE, globals.acceptor);
port = switch_sockaddr_get_port(sa);
ip_addr = switch_get_addr(ipbuf, sizeof (ipbuf), sa);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor listening on %s:%u\n", ip_addr, port);
/* try to initialize the erlang interface */
if (create_ei_cnode(ip_addr, globals.ei_nodename, &globals.ei_cnode) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_SOCKERR;
}
/* tell the erlang port manager where we can be reached. this returns a file descriptor pointing to epmd or -1 */
if ((globals.epmdfd = ei_publish(&globals.ei_cnode, port)) == -1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
"Failed to publish port to epmd. Try starting it yourself or run an erl shell with the -sname or -name option.\n");
return SWITCH_STATUS_SOCKERR;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connected to epmd and published erlang cnode name %s at port %d\n", globals.ei_cnode.thisnodename, port);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_STANDARD_API(exec_api_cmd)
{
char *argv[1024] = { 0 };
int unknown_command = 1, argc = 0;
char *mycmd = NULL;
const char *usage_string = "USAGE:\n"
"--------------------------------------------------------------------------------------------------------------------\n"
"erlang status - provides an overview of the current status\n"
"erlang event_filter - lists the event headers that will be sent to Erlang nodes\n"
"erlang nodes list - lists connected Erlang nodes (usefull for monitoring tools)\n"
"erlang nodes count - provides a count of connected Erlang nodes (usefull for monitoring tools)\n"
"erlang node <node_name> disconnect - disconnects an Erlang node\n"
"erlang node <node_name> connection - Shows the connection info\n"
"erlang node <node_name> event_streams - lists the event streams for an Erlang node\n"
"erlang node <node_name> fetch_bindings - lists the XML fetch bindings for an Erlang node\n"
"---------------------------------------------------------------------------------------------------------------------\n";
if (zstr(cmd)) {
stream->write_function(stream, "%s", usage_string);
return SWITCH_STATUS_SUCCESS;
}
if (!(mycmd = strdup(cmd))) {
return SWITCH_STATUS_MEMERR;
}
if (!(argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
stream->write_function(stream, "%s", usage_string);
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
if (zstr(argv[0])) {
stream->write_function(stream, "%s", usage_string);
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
if (!strncmp(argv[0], "status", 6)) {
unknown_command = 0;
api_erlang_status(stream);
} else if (!strncmp(argv[0], "event_filter", 6)) {
unknown_command = 0;
api_erlang_event_filter(stream);
} else if (!strncmp(argv[0], "nodes", 6) && !zstr(argv[1])) {
if (!strncmp(argv[1], "list", 6)) {
unknown_command = 0;
api_erlang_nodes_list(stream);
} else if (!strncmp(argv[1], "count", 6)) {
unknown_command = 0;
api_erlang_nodes_count(stream);
}
} else if (!strncmp(argv[0], "node", 6) && !zstr(argv[1]) && !zstr(argv[2])) {
if (!strncmp(argv[2], "disconnect", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_DISCONNECT);
} else if (!strncmp(argv[2], "connection", 2)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_REMOTE_IP);
} else if (!strncmp(argv[2], "event_streams", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_STREAMS);
} else if (!strncmp(argv[2], "fetch_bindings", 6)) {
unknown_command = 0;
api_erlang_node_command(stream, argv[1], API_COMMAND_BINDINGS);
}
}
if (unknown_command) {
stream->write_function(stream, "%s", usage_string);
}
switch_safe_free(mycmd);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_LOAD_FUNCTION(mod_kazoo_load) {
switch_api_interface_t *api_interface = NULL;
switch_application_interface_t *app_interface = NULL;
memset(&globals, 0, sizeof(globals));
globals.pool = pool;
globals.ei_nodes = NULL;
if(config() != SWITCH_STATUS_SUCCESS) {
// TODO: what would we need to clean up here?
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Improper configuration!\n");
return SWITCH_STATUS_TERM;
}
if(create_acceptor() != SWITCH_STATUS_SUCCESS) {
// TODO: what would we need to clean up here
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to create erlang connection acceptor!\n");
close_socket(&globals.acceptor);
return SWITCH_STATUS_TERM;
}
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
/* create an api for cli debug commands */
SWITCH_ADD_API(api_interface, "erlang", KAZOO_DESC, exec_api_cmd, KAZOO_SYNTAX);
switch_console_set_complete("add erlang status");
switch_console_set_complete("add erlang event_filter");
switch_console_set_complete("add erlang nodes list");
switch_console_set_complete("add erlang nodes count");
switch_console_set_complete("add erlang node ::erlang::node disconnect");
switch_console_set_complete("add erlang node ::erlang::node connection");
switch_console_set_complete("add erlang node ::erlang::node event_streams");
switch_console_set_complete("add erlang node ::erlang::node fetch_bindings");
switch_console_add_complete_func("::erlang::node", api_complete_erlang_node);
switch_thread_rwlock_create(&globals.ei_nodes_lock, pool);
switch_set_flag(&globals, LFLAG_RUNNING);
/* create all XML fetch agents */
bind_fetch_agents();
/* add our modified commands */
add_kz_commands(module_interface, api_interface);
/* add our modified dptools */
add_kz_dptools(module_interface, app_interface);
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_kazoo_shutdown) {
int sanity = 0;
switch_console_set_complete("del erlang");
switch_console_del_complete_func("::erlang::node");
/* stop taking new requests and start shuting down the threads */
switch_clear_flag(&globals, LFLAG_RUNNING);
/* give everyone time to cleanly shutdown */
while (switch_atomic_read(&globals.threads)) {
switch_yield(100000);
if (++sanity >= 200) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to kill all threads, continuing. This probably wont end well.....good luck!\n");
break;
}
}
if (globals.event_filter) {
switch_core_hash_destroy(&globals.event_filter);
}
switch_thread_rwlock_wrlock(globals.ei_nodes_lock);
switch_thread_rwlock_unlock(globals.ei_nodes_lock);
switch_thread_rwlock_destroy(globals.ei_nodes_lock);
/* close the connection to epmd and the acceptor */
close_socketfd(&globals.epmdfd);
close_socket(&globals.acceptor);
/* remove all XML fetch agents */
unbind_fetch_agents();
/* Close the port we reserved for uPnP/Switch behind firewall, if necessary */
// if (globals.nat_map && switch_nat_get_type()) {
// switch_nat_del_mapping(globals.port, SWITCH_NAT_TCP);
// }
/* clean up our allocated preferences */
switch_safe_free(globals.ip);
switch_safe_free(globals.ei_cookie);
switch_safe_free(globals.ei_nodename);
switch_safe_free(globals.kazoo_var_prefix);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_RUNTIME_FUNCTION(mod_kazoo_runtime) {
switch_os_socket_t os_socket;
switch_atomic_inc(&globals.threads);
switch_os_sock_get(&os_socket, globals.acceptor);
while (switch_test_flag(&globals, LFLAG_RUNNING)) {
int nodefd;
ErlConnect conn;
/* zero out errno because ei_accept doesn't differentiate between a */
/* failed authentication or a socket failure, or a client version */
/* mismatch or a godzilla attack (and a godzilla attack is highly likely) */
errno = 0;
/* wait here for an erlang node to connect, timming out to check if our module is still running every now-and-again */
if ((nodefd = ei_accept_tmo(&globals.ei_cnode, (int) os_socket, &conn, globals.connection_timeout)) == ERL_ERROR) {
if (erl_errno == ETIMEDOUT) {
continue;
} else if (errno) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang connection acceptor socket error %d %d\n", erl_errno, errno);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"Erlang node connection failed - ensure your cookie matches '%s' and you are using a good nodename\n", globals.ei_cookie);
}
continue;
}
if (!switch_test_flag(&globals, LFLAG_RUNNING)) {
break;
}
/* NEW ERLANG NODE CONNECTION! Hello friend! */
new_kazoo_node(nodefd, &conn);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Erlang connection acceptor shut down\n");
switch_atomic_dec(&globals.threads);
return SWITCH_STATUS_TERM;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -0,0 +1,171 @@
#include <switch.h>
#include <ei.h>
#define MAX_ACL 100
#define CMD_BUFLEN 1024 * 1000
#define MAX_QUEUE_LEN 25000
#define MAX_MISSED 500
#define MAX_PID_CHARS 255
#define VERSION "mod_kazoo v1.2.10-14"
#define API_COMMAND_DISCONNECT 0
#define API_COMMAND_REMOTE_IP 1
#define API_COMMAND_STREAMS 2
#define API_COMMAND_BINDINGS 3
typedef enum {
LFLAG_RUNNING = (1 << 0)
} event_flag_t;
struct ei_send_msg_s {
ei_x_buff buf;
erlang_pid pid;
};
typedef struct ei_send_msg_s ei_send_msg_t;
struct ei_received_msg_s {
ei_x_buff buf;
erlang_msg msg;
};
typedef struct ei_received_msg_s ei_received_msg_t;
struct ei_event_binding_s {
char id[SWITCH_UUID_FORMATTED_LENGTH + 1];
switch_event_node_t *node;
switch_event_types_t type;
const char *subclass_name;
struct ei_event_binding_s *next;
};
typedef struct ei_event_binding_s ei_event_binding_t;
struct ei_event_stream_s {
switch_memory_pool_t *pool;
ei_event_binding_t *bindings;
switch_queue_t *queue;
switch_socket_t *acceptor;
switch_pollset_t *pollset;
switch_pollfd_t *pollfd;
switch_socket_t *socket;
switch_mutex_t *socket_mutex;
switch_bool_t connected;
char remote_ip[25];
uint16_t remote_port;
char local_ip[25];
uint16_t local_port;
erlang_pid pid;
uint32_t flags;
struct ei_event_stream_s *next;
};
typedef struct ei_event_stream_s ei_event_stream_t;
struct ei_node_s {
int nodefd;
switch_atomic_t pending_bgapi;
switch_atomic_t receive_handlers;
switch_memory_pool_t *pool;
ei_event_stream_t *event_streams;
switch_mutex_t *event_streams_mutex;
switch_queue_t *send_msgs;
switch_queue_t *received_msgs;
char *peer_nodename;
switch_time_t created_time;
switch_socket_t *socket;
char remote_ip[25];
uint16_t remote_port;
char local_ip[25];
uint16_t local_port;
uint32_t flags;
struct ei_node_s *next;
};
typedef struct ei_node_s ei_node_t;
struct globals_s {
switch_memory_pool_t *pool;
switch_atomic_t threads;
switch_socket_t *acceptor;
struct ei_cnode_s ei_cnode;
switch_thread_rwlock_t *ei_nodes_lock;
ei_node_t *ei_nodes;
switch_xml_binding_t *config_fetch_binding;
switch_xml_binding_t *directory_fetch_binding;
switch_xml_binding_t *dialplan_fetch_binding;
switch_xml_binding_t *chatplan_fetch_binding;
switch_xml_binding_t *channels_fetch_binding;
switch_hash_t *event_filter;
int epmdfd;
int num_worker_threads;
switch_bool_t nat_map;
switch_bool_t ei_shortname;
int ei_compat_rel;
char *ip;
char *ei_cookie;
char *ei_nodename;
char *kazoo_var_prefix;
int var_prefix_length;
uint32_t flags;
int send_all_headers;
int send_all_private_headers;
int connection_timeout;
int receive_timeout;
int receive_msg_preallocate;
int event_stream_preallocate;
int send_msg_batch;
short event_stream_framing;
} globals;
typedef struct globals_s globals_t;
/* kazoo_node.c */
switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn);
/* kazoo_event_stream.c */
ei_event_stream_t *find_event_stream(ei_event_stream_t *event_streams, const erlang_pid *from);
ei_event_stream_t *new_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from);
switch_status_t remove_event_stream(ei_event_stream_t **event_streams, const erlang_pid *from);
switch_status_t remove_event_streams(ei_event_stream_t **event_streams);
unsigned long get_stream_port(const ei_event_stream_t *event_stream);
switch_status_t add_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name);
switch_status_t remove_event_binding(ei_event_stream_t *event_stream, const switch_event_types_t event_type, const char *subclass_name);
switch_status_t remove_event_bindings(ei_event_stream_t *event_stream);
/* kazoo_fetch_agent.c */
switch_status_t bind_fetch_agents();
switch_status_t unbind_fetch_agents();
switch_status_t remove_xml_clients(ei_node_t *ei_node);
switch_status_t add_fetch_handler(ei_node_t *ei_node, erlang_pid *from, switch_xml_binding_t *binding);
switch_status_t remove_fetch_handlers(ei_node_t *ei_node, erlang_pid *from);
switch_status_t fetch_reply(char *uuid_str, char *xml_str, switch_xml_binding_t *binding);
switch_status_t handle_api_command_streams(ei_node_t *ei_node, switch_stream_handle_t *stream);
/* kazoo_utils.c */
void close_socket(switch_socket_t **sock);
void close_socketfd(int *sockfd);
switch_socket_t *create_socket(switch_memory_pool_t *pool);
switch_status_t create_ei_cnode(const char *ip_addr, const char *name, struct ei_cnode_s *ei_cnode);
switch_status_t ei_compare_pids(const erlang_pid *pid1, const erlang_pid *pid2);
void ei_encode_switch_event_headers(ei_x_buff *ebuf, switch_event_t *event);
void ei_link(ei_node_t *ei_node, erlang_pid * from, erlang_pid * to);
void ei_encode_switch_event(ei_x_buff * ebuf, switch_event_t *event);
int ei_helper_send(ei_node_t *ei_node, erlang_pid* to, ei_x_buff *buf);
int ei_decode_atom_safe(char *buf, int *index, char *dst);
int ei_decode_string_or_binary_limited(char *buf, int *index, int maxsize, char *dst);
int ei_decode_string_or_binary(char *buf, int *index, char **dst);
switch_hash_t *create_default_filter();
/* kazoo_commands.c */
void add_kz_commands(switch_loadable_module_interface_t **module_interface, switch_api_interface_t *api_interface);
/* kazoo_dptools.c */
void add_kz_dptools(switch_loadable_module_interface_t **module_interface, switch_application_interface_t *app_interface);
#define _ei_x_encode_string(buf, string) { ei_x_encode_binary(buf, string, strlen(string)); }
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/