Merge pull request #328 in FS/freeswitch from ~LAZEDO/freeswitch:feature/fs-7774 to master
* commit 'd66839debe4ca57eaff4d477d239f88a3e30c60d': FS-7774 add mod_kazoo #resolve
This commit is contained in:
commit
369a8855a0
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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@
|
|
@ -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>
|
|
@ -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");
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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:
|
||||
*/
|
|
@ -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
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
Loading…
Reference in New Issue