diff --git a/build/modules.conf.in b/build/modules.conf.in
index b95f20029e..dff299fb68 100644
--- a/build/modules.conf.in
+++ b/build/modules.conf.in
@@ -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
diff --git a/configure.ac b/configure.ac
index 3b58e14685..1fb30e18c6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/src/mod/event_handlers/mod_kazoo/Makefile.am b/src/mod/event_handlers/mod_kazoo/Makefile.am
new file mode 100644
index 0000000000..86d556ba77
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/Makefile.am
@@ -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@
diff --git a/src/mod/event_handlers/mod_kazoo/conf/autoload_configs/kazoo.conf.xml b/src/mod/event_handlers/mod_kazoo/conf/autoload_configs/kazoo.conf.xml
new file mode 100644
index 0000000000..b730523d4c
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/conf/autoload_configs/kazoo.conf.xml
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_commands.c b/src/mod/event_handlers/mod_kazoo/kazoo_commands.c
new file mode 100644
index 0000000000..ab9b65386b
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/kazoo_commands.c
@@ -0,0 +1,152 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Karl Anderson
+ * Darren Schreiber
+ *
+ *
+ * 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 " [value]"
+
+#define UUID_MULTISET_DESC "Set multiple variables"
+#define UUID_MULTISET_SYNTAX " =;=..."
+
+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");
+}
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c b/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c
new file mode 100644
index 0000000000..887b1ea32b
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/kazoo_dptools.c
@@ -0,0 +1,182 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Karl Anderson
+ * Darren Schreiber
+ *
+ *
+ * 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 "="
+
+#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 "[^^]= ="
+
+#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 ""
+
+#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 "[^^] "
+
+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);
+}
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c b/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c
new file mode 100644
index 0000000000..00eb06a126
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/kazoo_event_stream.c
@@ -0,0 +1,584 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Karl Anderson
+ * Darren Schreiber
+ *
+ *
+ * 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:
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c b/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c
new file mode 100644
index 0000000000..522722c700
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/kazoo_fetch_agent.c
@@ -0,0 +1,707 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Karl Anderson
+ * Darren Schreiber
+ *
+ *
+ * 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:
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_node.c b/src/mod/event_handlers/mod_kazoo/kazoo_node.c
new file mode 100644
index 0000000000..0ffd3ffdf0
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/kazoo_node.c
@@ -0,0 +1,1254 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II
+ * Andrew Thompson
+ * Rob Charlton
+ * Darren Schreiber
+ * Mike Jerris
+ * Tamas Cseke
+ *
+ *
+ * handle_msg.c -- handle messages received from erlang nodes
+ *
+ */
+#include "mod_kazoo.h"
+
+struct api_command_struct_s {
+ char *cmd;
+ char *arg;
+ ei_node_t *ei_node;
+ char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
+ erlang_pid pid;
+ switch_memory_pool_t *pool;
+};
+typedef struct api_command_struct_s api_command_struct_t;
+
+static char *REQUEST_ATOMS[] = {
+ "noevents",
+ "exit",
+ "link",
+ "nixevent",
+ "sendevent",
+ "sendmsg",
+ "bind",
+ "getpid",
+ "version",
+ "bgapi",
+ "api",
+ "event",
+ "fetch_reply"
+};
+
+typedef enum {
+ REQUEST_NOEVENTS,
+ REQUEST_EXIT,
+ REQUEST_LINK,
+ REQUEST_NIXEVENT,
+ REQUEST_SENDEVENT,
+ REQUEST_SENDMSG,
+ REQUEST_BIND,
+ REQUEST_GETPID,
+ REQUEST_VERSION,
+ REQUEST_BGAPI,
+ REQUEST_API,
+ REQUEST_EVENT,
+ REQUEST_FETCH_REPLY,
+ REQUEST_MAX
+} request_atoms_t;
+
+static switch_status_t find_request(char *atom, int *request) {
+ for (int i = 0; i < REQUEST_MAX; i++) {
+ if(!strncmp(atom, REQUEST_ATOMS[i], MAXATOMLEN)) {
+ *request = i;
+ return SWITCH_STATUS_SUCCESS;
+ }
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
+static void destroy_node_handler(ei_node_t *ei_node) {
+ int pending = 0;
+ void *pop;
+
+ switch_clear_flag(ei_node, LFLAG_RUNNING);
+
+ /* wait for pending bgapi requests to complete */
+ while ((pending = switch_atomic_read(&ei_node->pending_bgapi))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for %d pending bgapi requests to complete\n", pending);
+ switch_yield(500000);
+ }
+
+ /* wait for receive handlers to complete */
+ while ((pending = switch_atomic_read(&ei_node->receive_handlers))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for %d receive handlers to complete\n", pending);
+ switch_yield(500000);
+ }
+
+ switch_mutex_lock(ei_node->event_streams_mutex);
+ remove_event_streams(&ei_node->event_streams);
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+
+ remove_xml_clients(ei_node);
+
+ while (switch_queue_trypop(ei_node->received_msgs, &pop) == SWITCH_STATUS_SUCCESS) {
+ ei_received_msg_t *received_msg = (ei_received_msg_t *) pop;
+
+ ei_x_free(&received_msg->buf);
+ switch_safe_free(received_msg);
+ }
+
+ while (switch_queue_trypop(ei_node->send_msgs, &pop) == SWITCH_STATUS_SUCCESS) {
+ ei_send_msg_t *send_msg = (ei_send_msg_t *) pop;
+
+ ei_x_free(&send_msg->buf);
+ switch_safe_free(send_msg);
+ }
+
+ close_socketfd(&ei_node->nodefd);
+
+ switch_mutex_destroy(ei_node->event_streams_mutex);
+
+ switch_core_destroy_memory_pool(&ei_node->pool);
+}
+
+static switch_status_t add_to_ei_nodes(ei_node_t *this_ei_node) {
+ switch_thread_rwlock_wrlock(globals.ei_nodes_lock);
+
+ if (!globals.ei_nodes) {
+ globals.ei_nodes = this_ei_node;
+ } else {
+ this_ei_node->next = globals.ei_nodes;
+ globals.ei_nodes = this_ei_node;
+ }
+
+ switch_thread_rwlock_unlock(globals.ei_nodes_lock);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t remove_from_ei_nodes(ei_node_t *this_ei_node) {
+ ei_node_t *ei_node, *prev = NULL;
+ int found = 0;
+
+ switch_thread_rwlock_wrlock(globals.ei_nodes_lock);
+
+ /* try to find the event bindings list for the requestor */
+ ei_node = globals.ei_nodes;
+ while(ei_node != NULL) {
+ if (ei_node == this_ei_node) {
+ found = 1;
+ break;
+ }
+
+ prev = ei_node;
+ ei_node = ei_node->next;
+ }
+
+ if (found) {
+ if (!prev) {
+ globals.ei_nodes = this_ei_node->next;
+ } else {
+ prev->next = ei_node->next;
+ }
+ }
+
+ switch_thread_rwlock_unlock(globals.ei_nodes_lock);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t api_exec(char *cmd, char *arg, char **reply) {
+ switch_stream_handle_t stream = { 0 };
+ switch_status_t status = SWITCH_STATUS_FALSE;
+
+ SWITCH_STANDARD_STREAM(stream);
+
+ if (switch_api_execute(cmd, arg, NULL, &stream) != SWITCH_STATUS_SUCCESS) {
+ *reply = switch_mprintf("%s: Command not found", cmd);
+ status = SWITCH_STATUS_NOTFOUND;
+ } else if (!stream.data || !strlen(stream.data)) {
+ *reply = switch_mprintf("%s: Command returned no output", cmd);
+ status = SWITCH_STATUS_FALSE;
+ } else {
+ *reply = strdup(stream.data);
+ status = SWITCH_STATUS_SUCCESS;
+ }
+
+ /* if the reply starts with the char "-" (the start of -USAGE ...) */
+ /* the args were missing or incorrect */
+ if (**reply == '-') {
+ status = SWITCH_STATUS_FALSE;
+ }
+
+ switch_safe_free(stream.data);
+
+ return status;
+}
+
+static void *SWITCH_THREAD_FUNC bgapi_exec(switch_thread_t *thread, void *obj) {
+ api_command_struct_t *acs = (api_command_struct_t *) obj;
+ switch_memory_pool_t *pool = acs->pool;
+ char *reply = NULL;
+ char *cmd = acs->cmd;
+ char *arg = acs->arg;
+ ei_node_t *ei_node = acs->ei_node;
+ ei_send_msg_t *send_msg;
+
+ switch_malloc(send_msg, sizeof(*send_msg));
+ memcpy(&send_msg->pid, &acs->pid, sizeof(erlang_pid));
+
+ if(!switch_test_flag(ei_node, LFLAG_RUNNING) || !switch_test_flag(&globals, LFLAG_RUNNING)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring command while shuting down\n");
+ switch_atomic_dec(&ei_node->pending_bgapi);
+ return NULL;
+ }
+
+ ei_x_new_with_version(&send_msg->buf);
+
+ ei_x_encode_tuple_header(&send_msg->buf, 3);
+
+ if (api_exec(cmd, arg, &reply) == SWITCH_STATUS_SUCCESS) {
+ ei_x_encode_atom(&send_msg->buf, "bgok");
+ } else {
+ ei_x_encode_atom(&send_msg->buf, "bgerror");
+ }
+
+ _ei_x_encode_string(&send_msg->buf, acs->uuid_str);
+ _ei_x_encode_string(&send_msg->buf, reply);
+
+ if (switch_queue_trypush(ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send bgapi response %s to %s <%d.%d.%d>\n"
+ ,acs->uuid_str
+ ,acs->pid.node
+ ,acs->pid.creation
+ ,acs->pid.num
+ ,acs->pid.serial);
+ ei_x_free(&send_msg->buf);
+ switch_safe_free(send_msg);
+ }
+
+ switch_atomic_dec(&ei_node->pending_bgapi);
+
+ switch_safe_free(reply);
+ switch_safe_free(acs->arg);
+ switch_core_destroy_memory_pool(&pool);
+
+ return NULL;
+}
+
+static void log_sendmsg_request(char *uuid, switch_event_t *event)
+{
+ char *cmd = switch_event_get_header(event, "call-command");
+ unsigned long cmd_hash;
+ switch_ssize_t hlen = -1;
+ unsigned long CMD_EXECUTE = switch_hashfunc_default("execute", &hlen);
+ unsigned long CMD_XFEREXT = switch_hashfunc_default("xferext", &hlen);
+ // unsigned long CMD_HANGUP = switch_hashfunc_default("hangup", &hlen);
+ // unsigned long CMD_NOMEDIA = switch_hashfunc_default("nomedia", &hlen);
+ // unsigned long CMD_UNICAST = switch_hashfunc_default("unicast", &hlen);
+
+ if (zstr(cmd)) {
+ return;
+ }
+
+ cmd_hash = switch_hashfunc_default(cmd, &hlen);
+
+ if (cmd_hash == CMD_EXECUTE) {
+ char *app_name = switch_event_get_header(event, "execute-app-name");
+ char *app_arg = switch_event_get_header(event, "execute-app-arg");
+
+ if(app_name) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "log|%s|executing %s %s \n", uuid, app_name, switch_str_nil(app_arg));
+ }
+ } else if (cmd_hash == CMD_XFEREXT) {
+ switch_event_header_t *hp;
+
+ for (hp = event->headers; hp; hp = hp->next) {
+ char *app_name;
+ char *app_arg;
+
+ if (!strcasecmp(hp->name, "application")) {
+ app_name = strdup(hp->value);
+ app_arg = strchr(app_name, ' ');
+
+ if (app_arg) {
+ *app_arg++ = '\0';
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "log|%s|building xferext extension: %s %s\n", uuid, app_name, app_arg);
+ }
+ }
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "log|%s|transfered call to xferext extension\n", uuid);
+ }
+}
+
+static switch_status_t build_event(switch_event_t *event, ei_x_buff * buf) {
+ int propslist_length, arity;
+
+ if(!event) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (ei_decode_list_header(buf->buff, &buf->index, &propslist_length)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity)) {
+ char key[1024];
+ char *value;
+
+ if (arity != 2) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(key), key)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (ei_decode_string_or_binary(buf->buff, &buf->index, &value)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (!strcmp(key, "body")) {
+ switch_safe_free(event->body);
+ event->body = value;
+ } else {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM | SWITCH_STACK_NODUP, key, value);
+ }
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t erlang_response_badarg(ei_x_buff * rbuf) {
+ if (rbuf) {
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_atom(rbuf, "error");
+ ei_x_encode_atom(rbuf, "badarg");
+ }
+
+ return SWITCH_STATUS_GENERR;
+}
+
+static switch_status_t erlang_response_baduuid(ei_x_buff * rbuf) {
+ if (rbuf) {
+ ei_x_format_wo_ver(rbuf, "{~a,~a}", "error", "baduuid");
+ }
+
+ return SWITCH_STATUS_NOTFOUND;
+}
+
+static switch_status_t erlang_response_notimplemented(ei_x_buff * rbuf) {
+ if (rbuf) {
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_atom(rbuf, "error");
+ ei_x_encode_atom(rbuf, "not_implemented");
+ }
+
+ return SWITCH_STATUS_NOTFOUND;
+}
+
+static switch_status_t erlang_response_ok(ei_x_buff *rbuf) {
+ if (rbuf) {
+ ei_x_encode_atom(rbuf, "ok");
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_request_noevents(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ ei_event_stream_t *event_stream;
+
+ switch_mutex_lock(ei_node->event_streams_mutex);
+ if ((event_stream = find_event_stream(ei_node->event_streams, pid))) {
+ remove_event_bindings(event_stream);
+ }
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+
+ return erlang_response_ok(rbuf);
+}
+
+static switch_status_t handle_request_exit(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ switch_clear_flag(ei_node, LFLAG_RUNNING);
+
+ return erlang_response_ok(rbuf);
+}
+
+static switch_status_t handle_request_link(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ ei_link(ei_node, ei_self(&globals.ei_cnode), pid);
+
+ return erlang_response_ok(rbuf);
+}
+
+static switch_status_t handle_request_nixevent(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char event_name[MAXATOMLEN + 1];
+ switch_event_types_t event_type;
+ ei_event_stream_t *event_stream;
+ int custom = 0, length = 0;
+
+ if (ei_decode_list_header(buf->buff, &buf->index, &length)
+ || length == 0) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch_mutex_lock(ei_node->event_streams_mutex);
+ if (!(event_stream = find_event_stream(ei_node->event_streams, pid))) {
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+ return erlang_response_ok(rbuf);
+ }
+
+ for (int i = 1; i <= length; i++) {
+ if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) {
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (custom) {
+ remove_event_binding(event_stream, SWITCH_EVENT_CUSTOM, event_name);
+ } else if (switch_name_event(event_name, &event_type) == SWITCH_STATUS_SUCCESS) {
+ switch (event_type) {
+ case SWITCH_EVENT_CUSTOM:
+ custom++;
+ break;
+ case SWITCH_EVENT_ALL:
+ for (switch_event_types_t type = 0; type < SWITCH_EVENT_ALL; type++) {
+ if(type != SWITCH_EVENT_CUSTOM) {
+ remove_event_binding(event_stream, type, NULL);
+ }
+ }
+ break;
+ default:
+ remove_event_binding(event_stream, event_type, NULL);
+ }
+ } else {
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+ return erlang_response_badarg(rbuf);
+ }
+ }
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+
+ return erlang_response_ok(rbuf);
+}
+
+static switch_status_t handle_request_sendevent(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char event_name[MAXATOMLEN + 1];
+ char subclass_name[MAXATOMLEN + 1];
+ switch_event_types_t event_type;
+ switch_event_t *event = NULL;
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)
+ || switch_name_event(event_name, &event_type) != SWITCH_STATUS_SUCCESS)
+ {
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (!strncasecmp(event_name, "CUSTOM", MAXATOMLEN)) {
+ if(ei_decode_atom(buf->buff, &buf->index, subclass_name)) {
+ return erlang_response_badarg(rbuf);
+ }
+ switch_event_create_subclass(&event, event_type, subclass_name);
+ } else {
+ switch_event_create(&event, event_type);
+ }
+
+ if (build_event(event, buf) == SWITCH_STATUS_SUCCESS) {
+ switch_event_fire(&event);
+ return erlang_response_ok(rbuf);
+ }
+
+ if(event) {
+ switch_event_destroy(&event);
+ }
+
+ return erlang_response_badarg(rbuf);
+}
+
+static switch_status_t handle_request_sendmsg(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ switch_core_session_t *session;
+ switch_event_t *event = NULL;
+ char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
+
+ if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(uuid_str), uuid_str)) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch_event_create(&event, SWITCH_EVENT_SEND_MESSAGE);
+ if (build_event(event, buf) != SWITCH_STATUS_SUCCESS) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ log_sendmsg_request(uuid_str, event);
+
+ if (zstr_buf(uuid_str) || !(session = switch_core_session_locate(uuid_str))) {
+ return erlang_response_baduuid(rbuf);
+ }
+ switch_core_session_queue_private_event(session, &event, SWITCH_FALSE);
+ switch_core_session_rwunlock(session);
+
+ return erlang_response_ok(rbuf);
+}
+
+static switch_status_t handle_request_bind(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char section_str[MAXATOMLEN + 1];
+ switch_xml_section_t section;
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, section_str)
+ || !(section = switch_xml_parse_section_string(section_str))) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch(section) {
+ case SWITCH_XML_SECTION_CONFIG:
+ add_fetch_handler(ei_node, pid, globals.config_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_DIRECTORY:
+ add_fetch_handler(ei_node, pid, globals.directory_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_DIALPLAN:
+ add_fetch_handler(ei_node, pid, globals.dialplan_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_CHATPLAN:
+ add_fetch_handler(ei_node, pid, globals.chatplan_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_CHANNELS:
+ add_fetch_handler(ei_node, pid, globals.channels_fetch_binding);
+ break;
+ default:
+ return erlang_response_badarg(rbuf);
+ }
+
+ return erlang_response_ok(rbuf);
+}
+
+static switch_status_t handle_request_getpid(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ if (rbuf) {
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_atom(rbuf, "ok");
+ ei_x_encode_pid(rbuf, ei_self(&globals.ei_cnode));
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_request_version(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ if (rbuf) {
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_atom(rbuf, "ok");
+ _ei_x_encode_string(rbuf, VERSION);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_request_bgapi(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ api_command_struct_t *acs = NULL;
+ switch_memory_pool_t *pool;
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+ switch_uuid_t uuid;
+ char cmd[MAXATOMLEN + 1];
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, cmd)) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch_core_new_memory_pool(&pool);
+ acs = switch_core_alloc(pool, sizeof(*acs));
+
+ if (ei_decode_string_or_binary(buf->buff, &buf->index, &acs->arg)) {
+ switch_core_destroy_memory_pool(&pool);
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "bgexec: %s(%s)\n", cmd, acs->arg);
+
+ acs->pool = pool;
+ acs->ei_node = ei_node;
+ acs->cmd = switch_core_strdup(pool, cmd);
+ memcpy(&acs->pid, pid, sizeof(erlang_pid));
+
+ switch_threadattr_create(&thd_attr, acs->pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+
+ switch_uuid_get(&uuid);
+ switch_uuid_format(acs->uuid_str, &uuid);
+ switch_thread_create(&thread, thd_attr, bgapi_exec, acs, acs->pool);
+
+ switch_atomic_inc(&ei_node->pending_bgapi);
+
+ if (rbuf) {
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_atom(rbuf, "ok");
+ _ei_x_encode_string(rbuf, acs->uuid_str);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_request_api(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char cmd[MAXATOMLEN + 1];
+ char *arg;
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, cmd)) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (ei_decode_string_or_binary(buf->buff, &buf->index, &arg)) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "exec: %s(%s)\n", cmd, arg);
+
+ if (rbuf) {
+ char *reply;
+
+ ei_x_encode_tuple_header(rbuf, 2);
+
+ if (api_exec(cmd, arg, &reply) == SWITCH_STATUS_SUCCESS) {
+ ei_x_encode_atom(rbuf, "ok");
+ } else {
+ ei_x_encode_atom(rbuf, "error");
+ }
+
+ _ei_x_encode_string(rbuf, reply);
+ switch_safe_free(reply);
+ }
+
+ switch_safe_free(arg);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_request_event(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char event_name[MAXATOMLEN + 1];
+ switch_event_types_t event_type;
+ ei_event_stream_t *event_stream;
+ int custom = 0, length = 0;
+
+ if (ei_decode_list_header(buf->buff, &buf->index, &length) || !length) {
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch_mutex_lock(ei_node->event_streams_mutex);
+ if (!(event_stream = find_event_stream(ei_node->event_streams, pid))) {
+ event_stream = new_event_stream(&ei_node->event_streams, pid);
+ /* ensure we are notified if the requesting processes dies so we can clean up */
+ ei_link(ei_node, ei_self(&globals.ei_cnode), pid);
+ }
+
+ for (int i = 1; i <= length; i++) {
+ if (ei_decode_atom_safe(buf->buff, &buf->index, event_name)) {
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (custom) {
+ add_event_binding(event_stream, SWITCH_EVENT_CUSTOM, event_name);
+ } else if (switch_name_event(event_name, &event_type) == SWITCH_STATUS_SUCCESS) {
+ switch (event_type) {
+ case SWITCH_EVENT_CUSTOM:
+ custom++;
+ break;
+ case SWITCH_EVENT_ALL:
+ for (switch_event_types_t type = 0; type < SWITCH_EVENT_ALL; type++) {
+ if(type != SWITCH_EVENT_CUSTOM) {
+ add_event_binding(event_stream, type, NULL);
+ }
+ }
+ break;
+ default:
+ add_event_binding(event_stream, event_type, NULL);
+ }
+ } else {
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+ return erlang_response_badarg(rbuf);
+ }
+ }
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+
+ if (rbuf) {
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_atom(rbuf, "ok");
+
+ ei_x_encode_tuple_header(rbuf, 2);
+ ei_x_encode_string(rbuf, ei_node->local_ip);
+ ei_x_encode_ulong(rbuf, get_stream_port(event_stream));
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_request_fetch_reply(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char section_str[MAXATOMLEN + 1];
+ char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
+ char *xml_str;
+ switch_xml_section_t section;
+ switch_status_t result;
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, section_str)
+ || !(section = switch_xml_parse_section_string(section_str))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring a fetch reply without a configuration section\n");
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (ei_decode_string_or_binary_limited(buf->buff, &buf->index, sizeof(uuid_str), uuid_str)
+ || zstr_buf(uuid_str)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring a fetch reply without request UUID\n");
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (ei_decode_string_or_binary(buf->buff, &buf->index, &xml_str)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring a fetch reply without XML\n");
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (zstr(xml_str)) {
+ switch_safe_free(xml_str);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Ignoring an empty fetch reply\n");
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch(section) {
+ case SWITCH_XML_SECTION_CONFIG:
+ result = fetch_reply(uuid_str, xml_str, globals.config_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_DIRECTORY:
+ result = fetch_reply(uuid_str, xml_str, globals.directory_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_DIALPLAN:
+ result = fetch_reply(uuid_str, xml_str, globals.dialplan_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_CHATPLAN:
+ result = fetch_reply(uuid_str, xml_str, globals.chatplan_fetch_binding);
+ break;
+ case SWITCH_XML_SECTION_CHANNELS:
+ result = fetch_reply(uuid_str, xml_str, globals.channels_fetch_binding);
+ break;
+ default:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved fetch reply for an unknown configuration section: %s\n", section_str);
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (result == SWITCH_STATUS_SUCCESS) {
+ return erlang_response_ok(rbuf);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved fetch reply for an unknown/expired UUID: %s\n", uuid_str);
+ return erlang_response_baduuid(rbuf);
+ }
+}
+
+static switch_status_t handle_kazoo_request(ei_node_t *ei_node, erlang_pid *pid, ei_x_buff *buf, ei_x_buff *rbuf) {
+ char atom[MAXATOMLEN + 1];
+ int type, size, arity = 0, request;
+
+ /* ...{_, _}} | ...atom()} = Buf */
+ ei_get_type(buf->buff, &buf->index, &type, &size);
+
+ /* is_tuple(Type) */
+ if (type == ERL_SMALL_TUPLE_EXT) {
+ /* ..._, _} = Buf */
+ ei_decode_tuple_header(buf->buff, &buf->index, &arity);
+ }
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, atom)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved mod_kazoo message that did not contain a command (ensure you are using Kazoo v2.14+).\n");
+ return erlang_response_badarg(rbuf);
+ }
+
+ if (find_request(atom, &request) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved mod_kazoo message for unimplemented feature (ensure you are using Kazoo v2.14+): %s\n", atom);
+ return erlang_response_badarg(rbuf);
+ }
+
+ switch(request) {
+ case REQUEST_NOEVENTS:
+ return handle_request_noevents(ei_node, pid, buf, rbuf);
+ case REQUEST_EXIT:
+ return handle_request_exit(ei_node, pid, buf, rbuf);
+ case REQUEST_LINK:
+ return handle_request_link(ei_node, pid, buf, rbuf);
+ case REQUEST_NIXEVENT:
+ return handle_request_nixevent(ei_node, pid, buf, rbuf);
+ case REQUEST_SENDEVENT:
+ return handle_request_sendevent(ei_node, pid, buf, rbuf);
+ case REQUEST_SENDMSG:
+ return handle_request_sendmsg(ei_node, pid, buf, rbuf);
+ case REQUEST_BIND:
+ return handle_request_bind(ei_node, pid, buf, rbuf);
+ case REQUEST_GETPID:
+ return handle_request_getpid(ei_node, pid, buf, rbuf);
+ case REQUEST_VERSION:
+ return handle_request_version(ei_node, pid, buf, rbuf);
+ case REQUEST_BGAPI:
+ return handle_request_bgapi(ei_node, pid, buf, rbuf);
+ case REQUEST_API:
+ return handle_request_api(ei_node, pid, buf, rbuf);
+ case REQUEST_EVENT:
+ return handle_request_event(ei_node, pid, buf, rbuf);
+ case REQUEST_FETCH_REPLY:
+ return handle_request_fetch_reply(ei_node, pid, buf, rbuf);
+ default:
+ return erlang_response_notimplemented(rbuf);
+ }
+}
+
+static switch_status_t handle_mod_kazoo_request(ei_node_t *ei_node, erlang_msg *msg, ei_x_buff *buf) {
+ char atom[MAXATOMLEN + 1];
+ int version, type, size, arity;
+
+ buf->index = 0;
+ ei_decode_version(buf->buff, &buf->index, &version);
+ ei_get_type(buf->buff, &buf->index, &type, &size);
+
+ /* is_tuple(Type) */
+ if (type != ERL_SMALL_TUPLE_EXT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received erlang message of an unexpected type (ensure you are using Kazoo v2.14+).\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ ei_decode_tuple_header(buf->buff, &buf->index, &arity);
+
+ if (ei_decode_atom_safe(buf->buff, &buf->index, atom)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved erlang message tuple that did not start with an atom (ensure you are using Kazoo v2.14+).\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* {'$gen_cast', {_, _}} = Buf */
+ if (arity == 2 && !strncmp(atom, "$gen_cast", 9)) {
+ return handle_kazoo_request(ei_node, &msg->from, buf, NULL);
+ /* {'$gen_call', {_, _}, {_, _}} = Buf */
+ } else if (arity == 3 && !strncmp(atom, "$gen_call", 9)) {
+ switch_status_t status;
+ ei_send_msg_t *send_msg;
+ erlang_ref ref;
+
+ switch_malloc(send_msg, sizeof(*send_msg));
+
+ ei_x_new(&send_msg->buf);
+
+ ei_x_new_with_version(&send_msg->buf);
+
+ /* ...{_, _}, {_, _}} = Buf */
+ ei_get_type(buf->buff, &buf->index, &type, &size);
+
+ /* is_tuple(Type) */
+ if (type != ERL_SMALL_TUPLE_EXT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received erlang call message of an unexpected type (ensure you are using Kazoo v2.14+).\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* ..._, _}, {_, _}} = Buf */
+ ei_decode_tuple_header(buf->buff, &buf->index, &arity);
+
+ /* ...pid(), _}, {_, _}} = Buf */
+ if (ei_decode_pid(buf->buff, &buf->index, &send_msg->pid)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received erlang call without a reply pid (ensure you are using Kazoo v2.14+).\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* ...ref()}, {_, _}} = Buf */
+ if (ei_decode_ref(buf->buff, &buf->index, &ref)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received erlang call without a reply tag (ensure you are using Kazoo v2.14+).\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* send_msg->buf = {ref(), ... */
+ ei_x_encode_tuple_header(&send_msg->buf, 2);
+ ei_x_encode_ref(&send_msg->buf, &ref);
+
+ status = handle_kazoo_request(ei_node, &msg->from, buf, &send_msg->buf);
+
+ if (switch_queue_trypush(ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) {
+ ei_x_free(&send_msg->buf);
+ switch_safe_free(send_msg);
+ }
+
+ return status;
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved inappropriate erlang message (ensure you are using Kazoo v2.14+)\n");
+ return SWITCH_STATUS_GENERR;
+ }
+}
+
+/* fake enough of the net_kernel module to be able to respond to net_adm:ping */
+static switch_status_t handle_net_kernel_request(ei_node_t *ei_node, erlang_msg *msg, ei_x_buff *buf) {
+ int version, size, type, arity;
+ char atom[MAXATOMLEN + 1];
+ ei_send_msg_t *send_msg;
+ erlang_ref ref;
+
+ switch_malloc(send_msg, sizeof(*send_msg));
+
+ ei_x_new(&send_msg->buf);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received net_kernel message, attempting to reply\n");
+
+ buf->index = 0;
+ ei_decode_version(buf->buff, &buf->index, &version);
+ ei_get_type(buf->buff, &buf->index, &type, &size);
+
+ /* is_tuple(Buff) */
+ if (type != ERL_SMALL_TUPLE_EXT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received net_kernel message of an unexpected type\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ ei_decode_tuple_header(buf->buff, &buf->index, &arity);
+
+ /* {_, _, _} = Buf */
+ if (arity != 3) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received net_kernel tuple has an unexpected arity\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* {'$gen_call', _, _} = Buf */
+ if (ei_decode_atom_safe(buf->buff, &buf->index, atom) || strncmp(atom, "$gen_call", 9)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received net_kernel message tuple does not begin with the atom '$gen_call'\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ ei_get_type(buf->buff, &buf->index, &type, &size);
+
+ /* {_, Sender, _}=Buff, is_tuple(Sender) */
+ if (type != ERL_SMALL_TUPLE_EXT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Second element of the net_kernel tuple is an unexpected type\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ ei_decode_tuple_header(buf->buff, &buf->index, &arity);
+
+ /* {_, _}=Sender */
+ if (arity != 2) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Second element of the net_kernel message has an unexpected arity\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* {Pid, Ref}=Sender */
+ if (ei_decode_pid(buf->buff, &buf->index, &send_msg->pid) || ei_decode_ref(buf->buff, &buf->index, &ref)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to decode erlang pid or ref of the net_kernel tuple second element\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ ei_get_type(buf->buff, &buf->index, &type, &size);
+
+ /* {_, _, Request}=Buff, is_tuple(Request) */
+ if (type != ERL_SMALL_TUPLE_EXT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Third element of the net_kernel message is an unexpected type\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ ei_decode_tuple_header(buf->buff, &buf->index, &arity);
+
+ /* {_, _}=Request */
+ if (arity != 2) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Third element of the net_kernel message has an unexpected arity\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* {is_auth, _}=Request */
+ if (ei_decode_atom_safe(buf->buff, &buf->index, atom) || strncmp(atom, "is_auth", MAXATOMLEN)) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "The net_kernel message third element does not begin with the atom 'is_auth'\n");
+ return SWITCH_STATUS_GENERR;
+ }
+
+ /* To ! {Tag, Reply} */
+ ei_x_new_with_version(&send_msg->buf);
+ ei_x_encode_tuple_header(&send_msg->buf, 2);
+ ei_x_encode_ref(&send_msg->buf, &ref);
+ ei_x_encode_atom(&send_msg->buf, "yes");
+
+ if (switch_queue_trypush(ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) {
+ ei_x_free(&send_msg->buf);
+ switch_safe_free(send_msg);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t handle_erl_send(ei_node_t *ei_node, erlang_msg *msg, ei_x_buff *buf) {
+ if (!strncmp(msg->toname, "net_kernel", MAXATOMLEN)) {
+ return handle_net_kernel_request(ei_node, msg, buf);
+ } else if (!strncmp(msg->toname, "mod_kazoo", MAXATOMLEN)) {
+ return handle_mod_kazoo_request(ei_node, msg, buf);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Recieved erlang message to unknown process \"%s\" (ensure you are using Kazoo v2.14+).\n", msg->toname);
+ return SWITCH_STATUS_GENERR;
+ }
+}
+
+static switch_status_t handle_erl_msg(ei_node_t *ei_node, erlang_msg *msg, ei_x_buff *buf) {
+ switch (msg->msgtype) {
+ case ERL_SEND:
+ case ERL_REG_SEND:
+ return handle_erl_send(ei_node, msg, buf);
+ case ERL_LINK:
+ /* we received an erlang link request? Should we be linking or are they linking to us and this just informs us? */
+ return SWITCH_STATUS_SUCCESS;
+ case ERL_UNLINK:
+ /* we received an erlang unlink request? Same question as the ERL_LINK, are we expected to do something? */
+ return SWITCH_STATUS_SUCCESS;
+ case ERL_EXIT:
+ /* we received a notice that a process we were linked to has exited, clean up any bindings */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received erlang exit notice for %s <%d.%d.%d>\n", msg->from.node, msg->from.creation, msg->from.num, msg->from.serial);
+
+ switch_mutex_lock(ei_node->event_streams_mutex);
+ remove_event_stream(&ei_node->event_streams, &msg->from);
+ switch_mutex_unlock(ei_node->event_streams_mutex);
+
+ remove_fetch_handlers(ei_node, &msg->from);
+ return SWITCH_STATUS_SUCCESS;
+ case ERL_EXIT2:
+ /* erlang nodes appear to send both the old and new style exit notices so just ignore these */
+ return SWITCH_STATUS_FALSE;
+ default:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Received unexpected erlang message type %d\n", (int) (msg->msgtype));
+ return SWITCH_STATUS_FALSE;
+ }
+}
+
+static void *SWITCH_THREAD_FUNC receive_handler(switch_thread_t *thread, void *obj) {
+ ei_node_t *ei_node = (ei_node_t *) obj;
+
+ switch_atomic_inc(&globals.threads);
+ switch_atomic_inc(&ei_node->receive_handlers);
+
+ switch_assert(ei_node != NULL);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting erlang receive handler %p: %s (%s:%d)\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+
+ while (switch_test_flag(ei_node, LFLAG_RUNNING) && switch_test_flag(&globals, LFLAG_RUNNING)) {
+ void *pop;
+
+ if (switch_queue_pop_timeout(ei_node->received_msgs, &pop, 500000) == SWITCH_STATUS_SUCCESS) {
+ ei_received_msg_t *received_msg = (ei_received_msg_t *) pop;
+ handle_erl_msg(ei_node, &received_msg->msg, &received_msg->buf);
+ ei_x_free(&received_msg->buf);
+ switch_safe_free(received_msg);
+ }
+ }
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutdown erlang receive handler %p: %s (%s:%d)\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+
+ switch_atomic_dec(&ei_node->receive_handlers);
+ switch_atomic_dec(&globals.threads);
+
+ return NULL;
+}
+
+static void *SWITCH_THREAD_FUNC handle_node(switch_thread_t *thread, void *obj) {
+ ei_node_t *ei_node = (ei_node_t *) obj;
+ ei_received_msg_t *received_msg = NULL;
+
+ switch_atomic_inc(&globals.threads);
+
+ switch_assert(ei_node != NULL);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Starting node request handler %p: %s (%s:%d)\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+
+ add_to_ei_nodes(ei_node);
+
+ while (switch_test_flag(ei_node, LFLAG_RUNNING) && switch_test_flag(&globals, LFLAG_RUNNING)) {
+ int status;
+ int send_msg_count = 0;
+ void *pop;
+
+ if (!received_msg) {
+ switch_malloc(received_msg, sizeof(*received_msg));
+ /* create a new buf for the erlang message and a rbuf for the reply */
+ if(globals.receive_msg_preallocate > 0) {
+ received_msg->buf.buff = malloc(globals.receive_msg_preallocate);
+ received_msg->buf.buffsz = globals.receive_msg_preallocate;
+ received_msg->buf.index = 0;
+ if(received_msg->buf.buff == NULL) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not pre-allocate memory for mod_kazoo message\n");
+ goto exit;
+ }
+ } else {
+ ei_x_new(&received_msg->buf);
+ }
+ }
+
+ while (switch_queue_trypop(ei_node->send_msgs, &pop) == SWITCH_STATUS_SUCCESS
+ && ++send_msg_count <= globals.send_msg_batch) {
+ ei_send_msg_t *send_msg = (ei_send_msg_t *) pop;
+ ei_helper_send(ei_node, &send_msg->pid, &send_msg->buf);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sent erlang message to %s <%d.%d.%d>\n"
+ ,send_msg->pid.node
+ ,send_msg->pid.creation
+ ,send_msg->pid.num
+ ,send_msg->pid.serial);
+ ei_x_free(&send_msg->buf);
+ switch_safe_free(send_msg);
+ }
+
+ /* wait for a erlang message, or timeout to check if the module is still running */
+ status = ei_xreceive_msg_tmo(ei_node->nodefd, &received_msg->msg, &received_msg->buf, globals.receive_timeout);
+
+ switch (status) {
+ case ERL_TICK:
+ /* erlang nodes send ticks to eachother to validate they are still reachable, we dont have to do anything here */
+ break;
+ case ERL_MSG:
+ if (switch_queue_trypush(ei_node->received_msgs, received_msg) != SWITCH_STATUS_SUCCESS) {
+ ei_x_free(&received_msg->buf);
+ switch_safe_free(received_msg);
+ }
+ if (globals.receive_msg_preallocate > 0 && received_msg->buf.buffsz > globals.receive_msg_preallocate) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "increased received message buffer size to %d\n", received_msg->buf.buffsz);
+ }
+ received_msg = NULL;
+ break;
+ case ERL_ERROR:
+ switch (erl_errno) {
+ case ETIMEDOUT:
+ case EAGAIN:
+ /* if ei_xreceive_msg_tmo just timed out, ignore it and let the while loop check if we are still running */
+ /* the erlang lib just wants us to try to receive again, so we will! */
+ break;
+ case EMSGSIZE:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang communication fault with node %p %s (%s:%d): my spoon is too big\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+ switch_clear_flag(ei_node, LFLAG_RUNNING);
+ break;
+ case EIO:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Erlang communication fault with node %p %s (%s:%d): socket closed or I/O error\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+ switch_clear_flag(ei_node, LFLAG_RUNNING);
+ break;
+ default:
+ /* OH NOS! something has gone horribly wrong, shutdown the connection if status set by ei_xreceive_msg_tmo is less than or equal to 0 */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Erlang communication fault with node %p %s (%s:%d): erl_errno=%d errno=%d\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, erl_errno, errno);
+ if (status < 0) {
+ switch_clear_flag(ei_node, LFLAG_RUNNING);
+ }
+ break;
+ }
+ break;
+ default:
+ /* HUH? didnt plan for this, whatevs shutdown the connection if status set by ei_xreceive_msg_tmo is less than or equal to 0 */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unexpected erlang receive status %p %s (%s:%d): %d\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port, status);
+ if (status < 0) {
+ switch_clear_flag(ei_node, LFLAG_RUNNING);
+ }
+ break;
+ }
+ }
+
+ exit:
+
+ if (received_msg) {
+ ei_x_free(&received_msg->buf);
+ switch_safe_free(received_msg);
+ }
+
+ remove_from_ei_nodes(ei_node);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Shutdown erlang node handler %p: %s (%s:%d)\n", (void *)ei_node, ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+
+ destroy_node_handler(ei_node);
+
+ switch_atomic_dec(&globals.threads);
+ return NULL;
+}
+
+/* Create a thread to wait for messages from an erlang node and process them */
+switch_status_t new_kazoo_node(int nodefd, ErlConnect *conn) {
+ switch_thread_t *thread;
+ switch_threadattr_t *thd_attr = NULL;
+ switch_memory_pool_t *pool = NULL;
+ switch_sockaddr_t *sa;
+ ei_node_t *ei_node;
+ int i = 0;
+
+ /* create memory pool for this erlang node */
+ if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory: Too bad drinking scotch isn't a paying job or Kenny's dad would be a millionare!\n");
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ /* from the erlang node's memory pool, allocate some memory for the structure */
+ if (!(ei_node = switch_core_alloc(pool, sizeof (*ei_node)))) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Out of memory: Stan, don't you know the first law of physics? Anything that's fun costs at least eight dollars.\n");
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ memset(ei_node, 0, sizeof(*ei_node));
+
+ /* store the location of our pool */
+ ei_node->pool = pool;
+
+ /* save the file descriptor that the erlang interface lib uses to communicate with the new node */
+ ei_node->nodefd = nodefd;
+ ei_node->peer_nodename = switch_core_strdup(ei_node->pool, conn->nodename);
+ ei_node->created_time = switch_micro_time_now();
+
+ /* store the IP and node name we are talking with */
+ switch_os_sock_put(&ei_node->socket, (switch_os_socket_t *)&nodefd, pool);
+
+ switch_socket_addr_get(&sa, SWITCH_TRUE, ei_node->socket);
+ ei_node->local_port = switch_sockaddr_get_port(sa);
+ switch_get_addr(ei_node->remote_ip, sizeof (ei_node->remote_ip), sa);
+
+ switch_socket_addr_get(&sa, SWITCH_FALSE, ei_node->socket);
+ ei_node->remote_port = switch_sockaddr_get_port(sa);
+ switch_get_addr(ei_node->local_ip, sizeof (ei_node->local_ip), sa);
+
+ switch_queue_create(&ei_node->send_msgs, MAX_QUEUE_LEN, pool);
+ switch_queue_create(&ei_node->received_msgs, MAX_QUEUE_LEN, pool);
+
+ switch_mutex_init(&ei_node->event_streams_mutex, SWITCH_MUTEX_DEFAULT, pool);
+
+ /* when we start we are running */
+ switch_set_flag(ei_node, LFLAG_RUNNING);
+
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "New erlang connection from node %s (%s:%d)\n", ei_node->peer_nodename, ei_node->remote_ip, ei_node->remote_port);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "New erlang connection to node %s (%s:%d)\n", ei_node->peer_nodename, ei_node->local_ip, ei_node->local_port);
+
+ for(i = 0; i < globals.num_worker_threads; i++) {
+ switch_threadattr_create(&thd_attr, ei_node->pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, receive_handler, ei_node, ei_node->pool);
+ }
+
+ switch_threadattr_create(&thd_attr, ei_node->pool);
+ switch_threadattr_detach_set(thd_attr, 1);
+ switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+ switch_thread_create(&thread, thd_attr, handle_node, ei_node, ei_node->pool);
+
+ 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:
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/kazoo_utils.c b/src/mod/event_handlers/mod_kazoo/kazoo_utils.c
new file mode 100644
index 0000000000..e1e356a752
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/kazoo_utils.c
@@ -0,0 +1,614 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Anthony Minessale II
+ * Andrew Thompson
+ * Rob Charlton
+ * Karl Anderson
+ *
+ * Original from mod_erlang_event.
+ * ei_helpers.c -- helper functions for ei
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#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:
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/mod_kazoo.c b/src/mod/event_handlers/mod_kazoo/mod_kazoo.c
new file mode 100644
index 0000000000..c0931e35c6
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/mod_kazoo.c
@@ -0,0 +1,776 @@
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2012, Anthony Minessale II
+ *
+ * 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
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Karl Anderson
+ * Darren Schreiber
+ *
+ *
+ * mod_kazoo.c -- Socket Controlled Event Handler
+ *
+ */
+#include "mod_kazoo.h"
+
+#define KAZOO_DESC "kazoo information"
+#define KAZOO_SYNTAX " []"
+
+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 disconnect - disconnects an Erlang node\n"
+ "erlang node connection - Shows the connection info\n"
+ "erlang node event_streams - lists the event streams for an Erlang node\n"
+ "erlang node 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:
+ */
diff --git a/src/mod/event_handlers/mod_kazoo/mod_kazoo.h b/src/mod/event_handlers/mod_kazoo/mod_kazoo.h
new file mode 100644
index 0000000000..d030feab52
--- /dev/null
+++ b/src/mod/event_handlers/mod_kazoo/mod_kazoo.h
@@ -0,0 +1,171 @@
+#include
+#include
+
+#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:
+ */