mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-09 05:41:51 +00:00
dfa78985b4
channel_variable: sdp_m_per_ptime Adds a new m= line for each distinct ptime in codec list. When this variable is not set: When mixing codecs with various ptime in a codec list, they will now be allowed to co-exist in the sdp but it will send no ptime attr. This means the ptime preferences on the offer will be ignored when mixing codecs with various ptimes. When receiving a codec list with no ptime attr, the ptime will be chosen from local preference instead of assuming 20ms This means if offer contains PCMU with not ptime and FS has PCMU@40i Dynamic payloads will now start at 98 and increment per additional dynamic codec per call. So now you can add CELT@32000h,CELT@48000h and each one will be auto-assigned a dynamic paylaod type.
1927 lines
69 KiB
C
1927 lines
69 KiB
C
/*
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2005-2010, Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
*
|
|
*
|
|
* switch_loadable_module.c -- Loadable Modules
|
|
*
|
|
*/
|
|
|
|
#include <switch.h>
|
|
|
|
/* for apr_pstrcat */
|
|
#include <apr_strings.h>
|
|
|
|
/* for apr_env_get and apr_env_set */
|
|
#include <apr_env.h>
|
|
|
|
/* for apr file and directory handling */
|
|
#include <apr_file_io.h>
|
|
|
|
struct switch_loadable_module {
|
|
char *key;
|
|
char *filename;
|
|
int perm;
|
|
switch_loadable_module_interface_t *module_interface;
|
|
switch_dso_lib_t lib;
|
|
switch_module_load_t switch_module_load;
|
|
switch_module_runtime_t switch_module_runtime;
|
|
switch_module_shutdown_t switch_module_shutdown;
|
|
switch_memory_pool_t *pool;
|
|
switch_status_t status;
|
|
switch_thread_t *thread;
|
|
switch_bool_t shutting_down;
|
|
};
|
|
|
|
struct switch_loadable_module_container {
|
|
switch_hash_t *module_hash;
|
|
switch_hash_t *endpoint_hash;
|
|
switch_hash_t *codec_hash;
|
|
switch_hash_t *dialplan_hash;
|
|
switch_hash_t *timer_hash;
|
|
switch_hash_t *application_hash;
|
|
switch_hash_t *api_hash;
|
|
switch_hash_t *file_hash;
|
|
switch_hash_t *speech_hash;
|
|
switch_hash_t *asr_hash;
|
|
switch_hash_t *directory_hash;
|
|
switch_hash_t *chat_hash;
|
|
switch_hash_t *say_hash;
|
|
switch_hash_t *management_hash;
|
|
switch_hash_t *limit_hash;
|
|
switch_mutex_t *mutex;
|
|
switch_memory_pool_t *pool;
|
|
};
|
|
|
|
static struct switch_loadable_module_container loadable_modules;
|
|
static switch_status_t do_shutdown(switch_loadable_module_t *module, switch_bool_t shutdown, switch_bool_t unload, switch_bool_t fail_if_busy,
|
|
const char **err);
|
|
static switch_status_t switch_loadable_module_load_module_ex(char *dir, char *fname, switch_bool_t runtime, switch_bool_t global, const char **err);
|
|
|
|
static void *SWITCH_THREAD_FUNC switch_loadable_module_exec(switch_thread_t *thread, void *obj)
|
|
{
|
|
|
|
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
switch_core_thread_session_t *ts = obj;
|
|
switch_loadable_module_t *module = ts->objs[0];
|
|
int restarts;
|
|
|
|
switch_assert(thread != NULL);
|
|
switch_assert(module != NULL);
|
|
|
|
for (restarts = 0; status != SWITCH_STATUS_TERM && !module->shutting_down; restarts++) {
|
|
status = module->switch_module_runtime();
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Thread ended for %s\n", module->module_interface->module_name);
|
|
|
|
if (ts->pool) {
|
|
switch_memory_pool_t *pool = ts->pool;
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying Pool for %s\n", module->module_interface->module_name);
|
|
switch_core_destroy_memory_pool(&pool);
|
|
}
|
|
switch_thread_exit(thread, 0);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void switch_loadable_module_runtime(void)
|
|
{
|
|
switch_hash_index_t *hi;
|
|
void *val;
|
|
switch_loadable_module_t *module;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
for (hi = switch_hash_first(NULL, loadable_modules.module_hash); hi; hi = switch_hash_next(hi)) {
|
|
switch_hash_this(hi, NULL, NULL, &val);
|
|
module = (switch_loadable_module_t *) val;
|
|
|
|
if (module->switch_module_runtime) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting runtime thread for %s\n", module->module_interface->module_name);
|
|
module->thread = switch_core_launch_thread(switch_loadable_module_exec, module, loadable_modules.pool);
|
|
}
|
|
}
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
}
|
|
|
|
static switch_status_t switch_loadable_module_process(char *key, switch_loadable_module_t *new_module)
|
|
{
|
|
switch_event_t *event;
|
|
|
|
new_module->key = switch_core_strdup(new_module->pool, key);
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
switch_core_hash_insert(loadable_modules.module_hash, key, new_module);
|
|
|
|
if (new_module->module_interface->endpoint_interface) {
|
|
const switch_endpoint_interface_t *ptr;
|
|
for (ptr = new_module->module_interface->endpoint_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load endpoint interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Endpoint '%s'\n", ptr->interface_name);
|
|
switch_core_hash_insert(loadable_modules.endpoint_hash, ptr->interface_name, (const void *) ptr);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "endpoint");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->codec_interface) {
|
|
const switch_codec_implementation_t *impl;
|
|
const switch_codec_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->codec_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load codec interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
unsigned load_interface = 1;
|
|
for (impl = ptr->implementations; impl; impl = impl->next) {
|
|
if (!impl->iananame) {
|
|
load_interface = 0;
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
|
|
"Failed to load codec interface %s from %s due to no iana name in an implementation.\n", ptr->interface_name,
|
|
key);
|
|
break;
|
|
}
|
|
if (impl->decoded_bytes_per_packet > SWITCH_RECOMMENDED_BUFFER_SIZE) {
|
|
load_interface = 0;
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
|
|
"Failed to load codec interface %s from %s due to bytes per frame exceeding buffer size.\n", ptr->interface_name,
|
|
key);
|
|
break;
|
|
}
|
|
}
|
|
if (load_interface) {
|
|
for (impl = ptr->implementations; impl; impl = impl->next) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
|
|
"Adding Codec %s %d %s %dhz %dms %dbps\n",
|
|
impl->iananame, impl->ianacode,
|
|
ptr->interface_name, impl->actual_samples_per_second, impl->microseconds_per_packet / 1000, impl->bits_per_second);
|
|
if (!switch_core_hash_find(loadable_modules.codec_hash, impl->iananame)) {
|
|
switch_core_hash_insert(loadable_modules.codec_hash, impl->iananame, (const void *) ptr);
|
|
}
|
|
}
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "codec");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->dialplan_interface) {
|
|
const switch_dialplan_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->dialplan_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load dialplan interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Dialplan '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "dialplan");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.dialplan_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->timer_interface) {
|
|
const switch_timer_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->timer_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load timer interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Timer '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "timer");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.timer_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->application_interface) {
|
|
const switch_application_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->application_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load application interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Application '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "application");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->short_desc));
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.application_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->api_interface) {
|
|
const switch_api_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->api_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load api interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding API Function '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc));
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.api_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->file_interface) {
|
|
const switch_file_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->file_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load file interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
int i;
|
|
for (i = 0; ptr->extens[i]; i++) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding File Format '%s'\n", ptr->extens[i]);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "file");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->extens[i]);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.file_hash, ptr->extens[i], (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->speech_interface) {
|
|
const switch_speech_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->speech_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load speech interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Speech interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "speech");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.speech_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->asr_interface) {
|
|
const switch_asr_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->asr_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load asr interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding ASR interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "asr");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.asr_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->directory_interface) {
|
|
const switch_directory_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->directory_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load directory interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Directory interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "directory");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.directory_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->chat_interface) {
|
|
const switch_chat_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->chat_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load chat interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Chat interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "chat");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.chat_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->say_interface) {
|
|
const switch_say_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->say_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load say interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Say interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "say");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_insert(loadable_modules.say_hash, ptr->interface_name, (const void *) ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_module->module_interface->management_interface) {
|
|
const switch_management_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->management_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->relative_oid) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load management interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
if (switch_core_hash_find(loadable_modules.management_hash, ptr->relative_oid)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
|
|
"Failed to load management interface %s. OID %s already exists\n", key, ptr->relative_oid);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
|
|
"Adding Management interface '%s' OID[%s.%s]\n", key, FREESWITCH_OID_PREFIX, ptr->relative_oid);
|
|
switch_core_hash_insert(loadable_modules.management_hash, ptr->relative_oid, (const void *) ptr);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "management");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->relative_oid);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if (new_module->module_interface->limit_interface) {
|
|
const switch_limit_interface_t *ptr;
|
|
|
|
for (ptr = new_module->module_interface->limit_interface; ptr; ptr = ptr->next) {
|
|
if (!ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load limit interface from %s due to no interface name.\n", key);
|
|
} else {
|
|
if (switch_core_hash_find(loadable_modules.limit_hash, ptr->interface_name)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT,
|
|
"Failed to load limit interface %s. Name %s already exists\n", key, ptr->interface_name);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
|
|
"Adding Limit interface '%s'\n", ptr->interface_name);
|
|
switch_core_hash_insert(loadable_modules.limit_hash, ptr->interface_name, (const void *) ptr);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "limit");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", new_module->key);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "filename", new_module->filename);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
static switch_status_t switch_loadable_module_unprocess(switch_loadable_module_t *old_module)
|
|
{
|
|
switch_event_t *event;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
|
|
if (old_module->module_interface->endpoint_interface) {
|
|
const switch_endpoint_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->endpoint_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
|
|
switch_core_session_hupall_endpoint(ptr, SWITCH_CAUSE_MANAGER_REQUEST);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Endpoint '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "endpoint");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.endpoint_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->codec_interface) {
|
|
const switch_codec_implementation_t *impl;
|
|
const switch_codec_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->codec_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
unsigned load_interface = 1;
|
|
for (impl = ptr->implementations; impl; impl = impl->next) {
|
|
if (!impl->iananame) {
|
|
load_interface = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (load_interface) {
|
|
for (impl = ptr->implementations; impl; impl = impl->next) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
|
|
"Deleting Codec %s %d %s %dhz %dms\n",
|
|
impl->iananame, impl->ianacode,
|
|
ptr->interface_name, impl->actual_samples_per_second, impl->microseconds_per_packet / 1000);
|
|
switch_core_session_hupall_matching_var("read_codec", impl->iananame, SWITCH_CAUSE_MANAGER_REQUEST);
|
|
switch_core_session_hupall_matching_var("write_codec", impl->iananame, SWITCH_CAUSE_MANAGER_REQUEST);
|
|
if (switch_core_hash_find(loadable_modules.codec_hash, impl->iananame)) {
|
|
switch_core_hash_delete(loadable_modules.codec_hash, impl->iananame);
|
|
}
|
|
}
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "codec");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->dialplan_interface) {
|
|
const switch_dialplan_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->dialplan_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Dialplan '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "dialplan");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.dialplan_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->timer_interface) {
|
|
const switch_timer_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->timer_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Timer '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "timer");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.timer_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->application_interface) {
|
|
const switch_application_interface_t *ptr;
|
|
for (ptr = old_module->module_interface->application_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Application '%s'\n", ptr->interface_name);
|
|
switch_core_session_hupall_matching_var(SWITCH_CURRENT_APPLICATION_VARIABLE, ptr->interface_name, SWITCH_CAUSE_MANAGER_REQUEST);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "application");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->short_desc));
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.application_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->api_interface) {
|
|
const switch_api_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->api_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting API Function '%s'\n", ptr->interface_name);
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "api");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "description", switch_str_nil(ptr->desc));
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "syntax", switch_str_nil(ptr->syntax));
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.api_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->file_interface) {
|
|
const switch_file_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->file_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
int i;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
for (i = 0; ptr->extens[i]; i++) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting File Format '%s'\n", ptr->extens[i]);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "file");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->extens[i]);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.file_hash, ptr->extens[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->speech_interface) {
|
|
const switch_speech_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->speech_interface; ptr; ptr = ptr->next) {
|
|
|
|
if (ptr->interface_name) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Speech interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "speech");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.speech_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->asr_interface) {
|
|
const switch_asr_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->asr_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Asr interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "asr");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.asr_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->directory_interface) {
|
|
const switch_directory_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->directory_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Directory interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "directory");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.directory_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (old_module->module_interface->chat_interface) {
|
|
const switch_chat_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->chat_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Chat interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "chat");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.chat_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->say_interface) {
|
|
const switch_say_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->say_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write lock interface '%s' to wait for existing references.\n",
|
|
ptr->interface_name);
|
|
|
|
if (switch_thread_rwlock_trywrlock_timeout(ptr->rwlock, 10) == SWITCH_STATUS_SUCCESS) {
|
|
switch_thread_rwlock_unlock(ptr->rwlock);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Giving up on '%s' waiting for existing references.\n", ptr->interface_name);
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleting Say interface '%s'\n", ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "say");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
switch_core_hash_delete(loadable_modules.say_hash, ptr->interface_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->management_interface) {
|
|
const switch_management_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->management_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->relative_oid) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
|
|
"Deleting Management interface '%s' OID[%s.%s]\n", old_module->key, FREESWITCH_OID_PREFIX, ptr->relative_oid);
|
|
switch_core_hash_delete(loadable_modules.management_hash, ptr->relative_oid);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "management");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->relative_oid);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (old_module->module_interface->limit_interface) {
|
|
const switch_limit_interface_t *ptr;
|
|
|
|
for (ptr = old_module->module_interface->limit_interface; ptr; ptr = ptr->next) {
|
|
if (ptr->interface_name) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,
|
|
"Deleting Limit interface '%s'\n", ptr->interface_name);
|
|
switch_core_hash_delete(loadable_modules.limit_hash, ptr->interface_name);
|
|
if (switch_event_create(&event, SWITCH_EVENT_MODULE_UNLOAD) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "limit");
|
|
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "name", ptr->interface_name);
|
|
switch_event_fire(&event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
static switch_status_t switch_loadable_module_load_file(char *path, char *filename, switch_bool_t global, switch_loadable_module_t **new_module)
|
|
{
|
|
switch_loadable_module_t *module = NULL;
|
|
switch_dso_lib_t dso = NULL;
|
|
apr_status_t status = SWITCH_STATUS_SUCCESS;
|
|
switch_loadable_module_function_table_t *interface_struct_handle = NULL;
|
|
switch_loadable_module_function_table_t *mod_interface_functions = NULL;
|
|
char *struct_name = NULL;
|
|
switch_module_load_t load_func_ptr = NULL;
|
|
int loading = 1;
|
|
switch_loadable_module_interface_t *module_interface = NULL;
|
|
char *derr = NULL;
|
|
const char *err = NULL;
|
|
switch_memory_pool_t *pool = NULL;
|
|
switch_bool_t load_global = global;
|
|
|
|
switch_assert(path != NULL);
|
|
|
|
switch_core_new_memory_pool(&pool);
|
|
*new_module = NULL;
|
|
|
|
struct_name = switch_core_sprintf(pool, "%s_module_interface", filename);
|
|
|
|
#ifdef WIN32
|
|
dso = switch_dso_open("FreeSwitch.dll", load_global, &derr);
|
|
#elif defined (MACOSX) || defined(DARWIN)
|
|
dso = switch_dso_open(SWITCH_PREFIX_DIR "/lib/libfreeswitch.dylib", load_global, &derr);
|
|
#else
|
|
dso = switch_dso_open(NULL, load_global, &derr);
|
|
#endif
|
|
if (!derr && dso) {
|
|
interface_struct_handle = switch_dso_data_sym(dso, struct_name, &derr);
|
|
}
|
|
|
|
switch_safe_free(derr)
|
|
|
|
if (!interface_struct_handle) {
|
|
dso = switch_dso_open(path, load_global, &derr);
|
|
}
|
|
|
|
while (loading) {
|
|
if (derr) {
|
|
err = derr;
|
|
break;
|
|
}
|
|
|
|
if (!interface_struct_handle) {
|
|
interface_struct_handle = switch_dso_data_sym(dso, struct_name, &derr);
|
|
}
|
|
|
|
if (derr) {
|
|
err = derr;
|
|
break;
|
|
}
|
|
|
|
if (interface_struct_handle && interface_struct_handle->switch_api_version != SWITCH_API_VERSION) {
|
|
err = "Trying to load an out of date module, please rebuild the module.";
|
|
break;
|
|
}
|
|
|
|
if (!load_global && interface_struct_handle && switch_test_flag(interface_struct_handle, SMODF_GLOBAL_SYMBOLS)) {
|
|
load_global = SWITCH_TRUE;
|
|
switch_dso_destroy(&dso);
|
|
interface_struct_handle = NULL;
|
|
dso = switch_dso_open(path, load_global, &derr);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading module with global namespace at request of module\n");
|
|
continue;
|
|
}
|
|
|
|
if (interface_struct_handle) {
|
|
mod_interface_functions = interface_struct_handle;
|
|
load_func_ptr = mod_interface_functions->load;
|
|
}
|
|
|
|
if (load_func_ptr == NULL) {
|
|
err = "Cannot locate symbol 'switch_module_load' please make sure this is a valid module.";
|
|
break;
|
|
}
|
|
|
|
status = load_func_ptr(&module_interface, pool);
|
|
|
|
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_NOUNLOAD) {
|
|
err = "Module load routine returned an error";
|
|
module_interface = NULL;
|
|
break;
|
|
}
|
|
|
|
if ((module = switch_core_alloc(pool, sizeof(switch_loadable_module_t))) == 0) {
|
|
err = "Could not allocate memory\n";
|
|
abort();
|
|
}
|
|
|
|
if (status == SWITCH_STATUS_NOUNLOAD) {
|
|
module->perm++;
|
|
}
|
|
|
|
loading = 0;
|
|
}
|
|
|
|
|
|
if (err) {
|
|
if (pool) {
|
|
switch_core_destroy_memory_pool(&pool);
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Loading module %s\n**%s**\n", path, err);
|
|
switch_safe_free(derr);
|
|
return SWITCH_STATUS_GENERR;
|
|
}
|
|
|
|
module->pool = pool;
|
|
module->filename = switch_core_strdup(module->pool, path);
|
|
module->module_interface = module_interface;
|
|
module->switch_module_load = load_func_ptr;
|
|
|
|
if (mod_interface_functions) {
|
|
module->switch_module_shutdown = mod_interface_functions->shutdown;
|
|
module->switch_module_runtime = mod_interface_functions->runtime;
|
|
}
|
|
|
|
module->lib = dso;
|
|
|
|
*new_module = module;
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Successfully Loaded [%s]\n", module_interface->module_name);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_load_module(char *dir, char *fname, switch_bool_t runtime, const char **err)
|
|
{
|
|
return switch_loadable_module_load_module_ex(dir, fname, runtime, SWITCH_FALSE, err);
|
|
}
|
|
|
|
static switch_status_t switch_loadable_module_load_module_ex(char *dir, char *fname, switch_bool_t runtime, switch_bool_t global, const char **err)
|
|
{
|
|
switch_size_t len = 0;
|
|
char *path;
|
|
char *file, *dot;
|
|
switch_loadable_module_t *new_module = NULL;
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
#ifdef WIN32
|
|
const char *ext = ".dll";
|
|
#else
|
|
const char *ext = ".so";
|
|
#endif
|
|
|
|
*err = "";
|
|
|
|
if ((file = switch_core_strdup(loadable_modules.pool, fname)) == 0) {
|
|
*err = "allocation error";
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
if (switch_is_file_path(file)) {
|
|
path = switch_core_strdup(loadable_modules.pool, file);
|
|
file = (char *) switch_cut_path(file);
|
|
if ((dot = strchr(file, '.'))) {
|
|
*dot = '\0';
|
|
}
|
|
} else {
|
|
if ((dot = strchr(file, '.'))) {
|
|
*dot = '\0';
|
|
}
|
|
len = strlen(dir);
|
|
len += strlen(file);
|
|
len += 8;
|
|
path = (char *) switch_core_alloc(loadable_modules.pool, len);
|
|
switch_snprintf(path, len, "%s%s%s%s", dir, SWITCH_PATH_SEPARATOR, file, ext);
|
|
}
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
if (switch_core_hash_find(loadable_modules.module_hash, file)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Module %s Already Loaded!\n", file);
|
|
*err = "Module already loaded";
|
|
status = SWITCH_STATUS_FALSE;
|
|
} else if ((status = switch_loadable_module_load_file(path, file, global, &new_module)) == SWITCH_STATUS_SUCCESS) {
|
|
if ((status = switch_loadable_module_process(file, new_module)) == SWITCH_STATUS_SUCCESS && runtime) {
|
|
if (new_module->switch_module_runtime) {
|
|
new_module->thread = switch_core_launch_thread(switch_loadable_module_exec, new_module, new_module->pool);
|
|
}
|
|
} else if (status != SWITCH_STATUS_SUCCESS) {
|
|
*err = "module load routine returned an error";
|
|
}
|
|
} else {
|
|
*err = "module load file routine returned an error";
|
|
}
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_exists(const char *mod)
|
|
{
|
|
switch_status_t status;
|
|
|
|
if (zstr(mod)) {
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
if (switch_core_hash_find(loadable_modules.module_hash, mod)) {
|
|
status = SWITCH_STATUS_SUCCESS;
|
|
} else {
|
|
status = SWITCH_STATUS_FALSE;
|
|
}
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
return status;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_unload_module(char *dir, char *fname, switch_bool_t force, const char **err)
|
|
{
|
|
switch_loadable_module_t *module = NULL;
|
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
|
|
|
if (force) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Spin the barrel and pull the trigger.......!\n");
|
|
}
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
if ((module = switch_core_hash_find(loadable_modules.module_hash, fname))) {
|
|
if (module->perm) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Module is not unloadable.\n");
|
|
*err = "Module is not unloadable";
|
|
status = SWITCH_STATUS_NOUNLOAD;
|
|
goto end;
|
|
} else {
|
|
if ((status = do_shutdown(module, SWITCH_TRUE, SWITCH_TRUE, !force, err) != SWITCH_STATUS_SUCCESS)) {
|
|
goto end;
|
|
}
|
|
}
|
|
switch_core_hash_delete(loadable_modules.module_hash, fname);
|
|
} else {
|
|
*err = "No such module!";
|
|
status = SWITCH_STATUS_FALSE;
|
|
}
|
|
end:
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
if (force) {
|
|
switch_yield(1000000);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "PHEW!\n");
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_enumerate_available(const char *dir_path, switch_modulename_callback_func_t callback, void *user_data)
|
|
{
|
|
switch_dir_t *dir = NULL;
|
|
switch_status_t status;
|
|
char buffer[256];
|
|
const char *fname;
|
|
const char *fname_ext;
|
|
char *fname_base;
|
|
|
|
#ifdef WIN32
|
|
const char *ext = ".dll";
|
|
#else
|
|
const char *ext = ".so";
|
|
#endif
|
|
|
|
if ((status = switch_dir_open(&dir, dir_path, loadable_modules.pool)) != SWITCH_STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
while((fname = switch_dir_next_file(dir, buffer, sizeof(buffer)))) {
|
|
if ((fname_ext = strrchr(fname, '.'))) {
|
|
if (!strcmp(fname_ext, ext)) {
|
|
if (!(fname_base = switch_mprintf("%.*s", (int)(fname_ext-fname), fname))) {
|
|
status = SWITCH_STATUS_GENERR;
|
|
goto end;
|
|
}
|
|
callback(user_data, fname_base);
|
|
switch_safe_free(fname_base)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
end:
|
|
switch_dir_close(dir);
|
|
return status;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_enumerate_loaded(switch_modulename_callback_func_t callback, void *user_data)
|
|
{
|
|
switch_hash_index_t *hi;
|
|
void *val;
|
|
switch_loadable_module_t *module;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
for (hi = switch_hash_first(NULL, loadable_modules.module_hash); hi; hi = switch_hash_next(hi)) {
|
|
switch_hash_this(hi, NULL, NULL, &val);
|
|
module = (switch_loadable_module_t *) val;
|
|
|
|
callback(user_data, module->module_interface->module_name);
|
|
}
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_build_dynamic(char *filename,
|
|
switch_module_load_t switch_module_load,
|
|
switch_module_runtime_t switch_module_runtime,
|
|
switch_module_shutdown_t switch_module_shutdown, switch_bool_t runtime)
|
|
{
|
|
switch_loadable_module_t *module = NULL;
|
|
switch_module_load_t load_func_ptr = NULL;
|
|
int loading = 1;
|
|
const char *err = NULL;
|
|
switch_loadable_module_interface_t *module_interface = NULL;
|
|
switch_memory_pool_t *pool;
|
|
|
|
|
|
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
|
|
abort();
|
|
}
|
|
|
|
if ((module = switch_core_alloc(pool, sizeof(switch_loadable_module_t))) == 0) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Couldn't allocate memory\n");
|
|
abort();
|
|
}
|
|
|
|
|
|
|
|
while (loading) {
|
|
switch_status_t status;
|
|
load_func_ptr = (switch_module_load_t) switch_module_load;
|
|
|
|
if (load_func_ptr == NULL) {
|
|
err = "Cannot Load";
|
|
break;
|
|
}
|
|
|
|
status = load_func_ptr(&module_interface, pool);
|
|
|
|
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_NOUNLOAD) {
|
|
err = "Module load routine returned an error";
|
|
module_interface = NULL;
|
|
break;
|
|
}
|
|
|
|
if ((module = switch_core_alloc(pool, sizeof(switch_loadable_module_t))) == 0) {
|
|
err = "Could not allocate memory\n";
|
|
abort();
|
|
}
|
|
|
|
if (status == SWITCH_STATUS_NOUNLOAD) {
|
|
module->perm++;
|
|
}
|
|
|
|
loading = 0;
|
|
}
|
|
|
|
if (err) {
|
|
switch_core_destroy_memory_pool(&pool);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Error Loading module %s\n**%s**\n", filename, err);
|
|
return SWITCH_STATUS_GENERR;
|
|
}
|
|
|
|
module->pool = pool;
|
|
module->filename = switch_core_strdup(module->pool, filename);
|
|
module->module_interface = module_interface;
|
|
module->switch_module_load = load_func_ptr;
|
|
|
|
if (switch_module_shutdown) {
|
|
module->switch_module_shutdown = switch_module_shutdown;
|
|
}
|
|
if (switch_module_runtime) {
|
|
module->switch_module_runtime = switch_module_runtime;
|
|
}
|
|
if (runtime && module->switch_module_runtime) {
|
|
module->thread = switch_core_launch_thread(switch_loadable_module_exec, module, module->pool);
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Successfully Loaded [%s]\n", module_interface->module_name);
|
|
return switch_loadable_module_process((char *) module->filename, module);
|
|
}
|
|
|
|
#ifdef WIN32
|
|
static void switch_loadable_module_path_init()
|
|
{
|
|
char *path = NULL, *working = NULL;
|
|
apr_dir_t *perl_dir_handle = NULL;
|
|
|
|
apr_env_get(&path, "path", loadable_modules.pool);
|
|
apr_filepath_get(&working, APR_FILEPATH_NATIVE, loadable_modules.pool);
|
|
|
|
if (apr_dir_open(&perl_dir_handle, ".\\perl", loadable_modules.pool) == APR_SUCCESS) {
|
|
apr_dir_close(perl_dir_handle);
|
|
apr_env_set("path", apr_pstrcat(loadable_modules.pool, path, ";", working, "\\perl", NULL), loadable_modules.pool);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_loadable_module_init(switch_bool_t autoload)
|
|
{
|
|
|
|
apr_finfo_t finfo = { 0 };
|
|
apr_dir_t *module_dir_handle = NULL;
|
|
apr_int32_t finfo_flags = APR_FINFO_DIRENT | APR_FINFO_TYPE | APR_FINFO_NAME;
|
|
char *cf = "modules.conf";
|
|
char *pcf = "post_load_modules.conf";
|
|
switch_xml_t cfg, xml;
|
|
unsigned char all = 0;
|
|
unsigned int count = 0;
|
|
const char *err;
|
|
|
|
|
|
#ifdef WIN32
|
|
const char *ext = ".dll";
|
|
const char *EXT = ".DLL";
|
|
#elif defined (MACOSX) || defined (DARWIN)
|
|
const char *ext = ".dylib";
|
|
const char *EXT = ".DYLIB";
|
|
#else
|
|
const char *ext = ".so";
|
|
const char *EXT = ".SO";
|
|
#endif
|
|
|
|
memset(&loadable_modules, 0, sizeof(loadable_modules));
|
|
switch_core_new_memory_pool(&loadable_modules.pool);
|
|
|
|
|
|
#ifdef WIN32
|
|
switch_loadable_module_path_init();
|
|
#endif
|
|
|
|
switch_core_hash_init(&loadable_modules.module_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.endpoint_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.codec_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.timer_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.application_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.api_hash, loadable_modules.pool);
|
|
switch_core_hash_init(&loadable_modules.file_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.speech_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.asr_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.directory_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.chat_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.say_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.management_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.limit_hash, loadable_modules.pool);
|
|
switch_core_hash_init_nocase(&loadable_modules.dialplan_hash, loadable_modules.pool);
|
|
switch_mutex_init(&loadable_modules.mutex, SWITCH_MUTEX_NESTED, loadable_modules.pool);
|
|
|
|
switch_loadable_module_load_module("", "CORE_SOFTTIMER_MODULE", SWITCH_FALSE, &err);
|
|
switch_loadable_module_load_module("", "CORE_PCM_MODULE", SWITCH_FALSE, &err);
|
|
|
|
if (!autoload) return SWITCH_STATUS_SUCCESS;
|
|
|
|
if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
|
|
switch_xml_t mods, ld;
|
|
if ((mods = switch_xml_child(cfg, "modules"))) {
|
|
for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) {
|
|
switch_bool_t global = SWITCH_FALSE;
|
|
const char *val = switch_xml_attr_soft(ld, "module");
|
|
const char *path = switch_xml_attr_soft(ld, "path");
|
|
const char *critical = switch_xml_attr_soft(ld, "critical");
|
|
const char *sglobal = switch_xml_attr_soft(ld, "global");
|
|
if (zstr(val) || (strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT))) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val);
|
|
continue;
|
|
}
|
|
global = switch_true(sglobal);
|
|
|
|
if (path && zstr(path)) {
|
|
path = SWITCH_GLOBAL_dirs.mod_dir;
|
|
}
|
|
if (switch_loadable_module_load_module_ex((char *) path, (char *) val, SWITCH_FALSE, global, &err) == SWITCH_STATUS_FALSE) {
|
|
if (critical && switch_true(critical)) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Failed to load critical module '%s', abort()\n", val);
|
|
abort();
|
|
}
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
switch_xml_free(xml);
|
|
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "open of %s failed\n", cf);
|
|
}
|
|
|
|
if ((xml = switch_xml_open_cfg(pcf, &cfg, NULL))) {
|
|
switch_xml_t mods, ld;
|
|
|
|
if ((mods = switch_xml_child(cfg, "modules"))) {
|
|
for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) {
|
|
switch_bool_t global = SWITCH_FALSE;
|
|
const char *val = switch_xml_attr_soft(ld, "module");
|
|
const char *path = switch_xml_attr_soft(ld, "path");
|
|
const char *sglobal = switch_xml_attr_soft(ld, "global");
|
|
if (zstr(val) || (strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT))) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val);
|
|
continue;
|
|
}
|
|
global = switch_true(sglobal);
|
|
|
|
if (path && zstr(path)) {
|
|
path = SWITCH_GLOBAL_dirs.mod_dir;
|
|
}
|
|
switch_loadable_module_load_module_ex((char *) path, (char *) val, SWITCH_FALSE, global, &err);
|
|
count++;
|
|
}
|
|
}
|
|
switch_xml_free(xml);
|
|
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "open of %s failed\n", pcf);
|
|
}
|
|
|
|
if (!count) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "No modules loaded, assuming 'load all'\n");
|
|
all = 1;
|
|
}
|
|
|
|
if (all) {
|
|
if (apr_dir_open(&module_dir_handle, SWITCH_GLOBAL_dirs.mod_dir, loadable_modules.pool) != APR_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Can't open directory: %s\n", SWITCH_GLOBAL_dirs.mod_dir);
|
|
return SWITCH_STATUS_GENERR;
|
|
}
|
|
|
|
while (apr_dir_read(&finfo, finfo_flags, module_dir_handle) == APR_SUCCESS) {
|
|
const char *fname = finfo.fname;
|
|
|
|
if (finfo.filetype != APR_REG) {
|
|
continue;
|
|
}
|
|
|
|
if (!fname) {
|
|
fname = finfo.name;
|
|
}
|
|
|
|
if (!fname) {
|
|
continue;
|
|
}
|
|
|
|
if (zstr(fname) || (!strstr(fname, ext) && !strstr(fname, EXT))) {
|
|
continue;
|
|
}
|
|
|
|
switch_loadable_module_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, (char *) fname, SWITCH_FALSE, &err);
|
|
}
|
|
apr_dir_close(module_dir_handle);
|
|
}
|
|
|
|
switch_loadable_module_runtime();
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
static switch_status_t do_shutdown(switch_loadable_module_t *module, switch_bool_t shutdown, switch_bool_t unload, switch_bool_t fail_if_busy,
|
|
const char **err)
|
|
{
|
|
int32_t flags = switch_core_flags();
|
|
switch_assert(module != NULL);
|
|
|
|
if (fail_if_busy && module->module_interface->rwlock && switch_thread_rwlock_trywrlock(module->module_interface->rwlock) != SWITCH_STATUS_SUCCESS) {
|
|
if (err) {
|
|
*err = "Module in use.";
|
|
}
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Module %s is in use, cannot unload.\n", module->module_interface->module_name);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
module->shutting_down = SWITCH_TRUE;
|
|
|
|
if (shutdown) {
|
|
switch_loadable_module_unprocess(module);
|
|
if (module->switch_module_shutdown) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Stopping: %s\n", module->module_interface->module_name);
|
|
module->status = module->switch_module_shutdown();
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s has no shutdown routine\n", module->module_interface->module_name);
|
|
}
|
|
}
|
|
|
|
if (fail_if_busy && module->module_interface->rwlock) {
|
|
switch_thread_rwlock_unlock(module->module_interface->rwlock);
|
|
}
|
|
|
|
if (unload && module->status != SWITCH_STATUS_NOUNLOAD && !(flags & SCF_VG)) {
|
|
switch_memory_pool_t *pool;
|
|
switch_status_t st;
|
|
|
|
if (module->thread) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s stopping runtime thread.\n", module->module_interface->module_name);
|
|
switch_thread_join(&st, module->thread);
|
|
}
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s unloaded.\n", module->module_interface->module_name);
|
|
switch_dso_destroy(&module->lib);
|
|
if ((pool = module->pool)) {
|
|
module = NULL;
|
|
switch_core_destroy_memory_pool(&pool);
|
|
}
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
|
|
{
|
|
switch_hash_index_t *hi;
|
|
void *val;
|
|
switch_loadable_module_t *module;
|
|
|
|
if (!loadable_modules.module_hash) {
|
|
return;
|
|
}
|
|
|
|
for (hi = switch_hash_first(NULL, loadable_modules.module_hash); hi; hi = switch_hash_next(hi)) {
|
|
switch_hash_this(hi, NULL, NULL, &val);
|
|
module = (switch_loadable_module_t *) val;
|
|
if (!module->perm) {
|
|
do_shutdown(module, SWITCH_TRUE, SWITCH_FALSE, SWITCH_FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
switch_yield(1000000);
|
|
|
|
for (hi = switch_hash_first(NULL, loadable_modules.module_hash); hi; hi = switch_hash_next(hi)) {
|
|
switch_hash_this(hi, NULL, NULL, &val);
|
|
module = (switch_loadable_module_t *) val;
|
|
if (!module->perm) {
|
|
do_shutdown(module, SWITCH_FALSE, SWITCH_TRUE, SWITCH_FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
switch_core_hash_destroy(&loadable_modules.module_hash);
|
|
switch_core_hash_destroy(&loadable_modules.endpoint_hash);
|
|
switch_core_hash_destroy(&loadable_modules.codec_hash);
|
|
switch_core_hash_destroy(&loadable_modules.timer_hash);
|
|
switch_core_hash_destroy(&loadable_modules.application_hash);
|
|
switch_core_hash_destroy(&loadable_modules.api_hash);
|
|
switch_core_hash_destroy(&loadable_modules.file_hash);
|
|
switch_core_hash_destroy(&loadable_modules.speech_hash);
|
|
switch_core_hash_destroy(&loadable_modules.asr_hash);
|
|
switch_core_hash_destroy(&loadable_modules.directory_hash);
|
|
switch_core_hash_destroy(&loadable_modules.chat_hash);
|
|
switch_core_hash_destroy(&loadable_modules.say_hash);
|
|
switch_core_hash_destroy(&loadable_modules.management_hash);
|
|
switch_core_hash_destroy(&loadable_modules.limit_hash);
|
|
switch_core_hash_destroy(&loadable_modules.dialplan_hash);
|
|
|
|
switch_core_destroy_memory_pool(&loadable_modules.pool);
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_endpoint_interface_t *) switch_loadable_module_get_endpoint_interface(const char *name)
|
|
{
|
|
switch_endpoint_interface_t *ptr;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
ptr = switch_core_hash_find(loadable_modules.endpoint_hash, name);
|
|
if (ptr) {
|
|
PROTECT_INTERFACE(ptr);
|
|
}
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
|
|
return ptr;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_codec_interface_t *) switch_loadable_module_get_codec_interface(const char *name)
|
|
{
|
|
char altname[256] = "";
|
|
switch_codec_interface_t *codec;
|
|
switch_size_t x;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
if (!(codec = switch_core_hash_find(loadable_modules.codec_hash, name))) {
|
|
for (x = 0; x < strlen(name); x++) {
|
|
altname[x] = (char) toupper((int) name[x]);
|
|
}
|
|
if (!(codec = switch_core_hash_find(loadable_modules.codec_hash, altname))) {
|
|
for (x = 0; x < strlen(name); x++) {
|
|
altname[x] = (char) tolower((int) name[x]);
|
|
}
|
|
codec = switch_core_hash_find(loadable_modules.codec_hash, altname);
|
|
}
|
|
}
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
if (codec) {
|
|
PROTECT_INTERFACE(codec);
|
|
}
|
|
|
|
return codec;
|
|
}
|
|
|
|
#define HASH_FUNC(_kind_) SWITCH_DECLARE(switch_##_kind_##_interface_t *) switch_loadable_module_get_##_kind_##_interface(const char *name) \
|
|
{ \
|
|
switch_##_kind_##_interface_t *i; \
|
|
if ((i = switch_core_hash_find_locked(loadable_modules._kind_##_hash, name, loadable_modules.mutex))) { \
|
|
PROTECT_INTERFACE(i); \
|
|
} \
|
|
return i; \
|
|
}
|
|
|
|
HASH_FUNC(dialplan)
|
|
HASH_FUNC(timer)
|
|
HASH_FUNC(application)
|
|
HASH_FUNC(api)
|
|
HASH_FUNC(file)
|
|
HASH_FUNC(speech)
|
|
HASH_FUNC(asr)
|
|
HASH_FUNC(directory)
|
|
HASH_FUNC(chat)
|
|
HASH_FUNC(limit)
|
|
|
|
|
|
SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interface(const char *name)
|
|
{
|
|
return switch_core_hash_find_locked(loadable_modules.say_hash, name, loadable_modules.mutex);
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_management_interface_t *) switch_loadable_module_get_management_interface(const char *relative_oid)
|
|
{
|
|
return switch_core_hash_find_locked(loadable_modules.management_hash, relative_oid, loadable_modules.mutex);
|
|
}
|
|
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
static void do_print(const switch_codec_implementation_t **array, int arraylen)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < arraylen; i++) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
|
|
"DEBUG %d %s:%d %d\n", i, array[i]->iananame, array[i]->ianacode, array[i]->microseconds_per_packet / 1000);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
/* helper only -- bounds checking enforced by caller */
|
|
static void do_swap(const switch_codec_implementation_t **array, int a, int b)
|
|
{
|
|
const switch_codec_implementation_t *tmp = array[b];
|
|
array[b] = array[a];
|
|
array[a] = tmp;
|
|
}
|
|
|
|
static void switch_loadable_module_sort_codecs(const switch_codec_implementation_t **array, int arraylen)
|
|
{
|
|
int i = 0, sorted_ptime = 0;
|
|
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--BEFORE\n");
|
|
do_print(array, arraylen);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--BEFORE\n");
|
|
#endif
|
|
|
|
for (i = 0; i < arraylen; i++) {
|
|
int this_ptime = array[i]->microseconds_per_packet / 1000;
|
|
|
|
if (!sorted_ptime) {
|
|
sorted_ptime = this_ptime;
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sorted1 = %d\n", sorted_ptime);
|
|
#endif
|
|
}
|
|
|
|
if (this_ptime != sorted_ptime) {
|
|
int j;
|
|
int swapped = 0;
|
|
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%d != %d\n", this_ptime, sorted_ptime);
|
|
#endif
|
|
for(j = i; j < arraylen; j++) {
|
|
int check_ptime = array[j]->microseconds_per_packet / 1000;
|
|
|
|
if (check_ptime == sorted_ptime) {
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "swap %d %d ptime %d\n", i, j, check_ptime);
|
|
#endif
|
|
do_swap(array, i, j);
|
|
swapped = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!swapped) {
|
|
sorted_ptime = this_ptime;
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sorted2 = %d\n", sorted_ptime);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_CODEC_SORTING
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--AFTER\n");
|
|
do_print(array, arraylen);
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "--AFTER\n");
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
SWITCH_DECLARE(int) switch_loadable_module_get_codecs(const switch_codec_implementation_t **array, int arraylen)
|
|
{
|
|
switch_hash_index_t *hi;
|
|
void *val;
|
|
switch_codec_interface_t *codec_interface;
|
|
int i = 0;
|
|
const switch_codec_implementation_t *imp;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
for (hi = switch_hash_first(NULL, loadable_modules.codec_hash); hi; hi = switch_hash_next(hi)) {
|
|
switch_hash_this(hi, NULL, NULL, &val);
|
|
codec_interface = (switch_codec_interface_t *) val;
|
|
|
|
/* Look for the default ptime of the codec because it's the safest choice */
|
|
for (imp = codec_interface->implementations; imp; imp = imp->next) {
|
|
uint32_t default_ptime = switch_default_ptime(imp->iananame, imp->ianacode);
|
|
|
|
if (imp->microseconds_per_packet / 1000 == (int)default_ptime) {
|
|
array[i++] = imp;
|
|
goto found;
|
|
}
|
|
}
|
|
/* oh well we will use what we have */
|
|
array[i++] = codec_interface->implementations;
|
|
|
|
found:
|
|
|
|
if (i > arraylen) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
switch_loadable_module_sort_codecs(array, i);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
SWITCH_DECLARE(int) switch_loadable_module_get_codecs_sorted(const switch_codec_implementation_t **array, int arraylen, char **prefs, int preflen)
|
|
{
|
|
int x, i = 0;
|
|
switch_codec_interface_t *codec_interface;
|
|
const switch_codec_implementation_t *imp;
|
|
|
|
switch_mutex_lock(loadable_modules.mutex);
|
|
|
|
for (x = 0; x < preflen; x++) {
|
|
char *cur, *last = NULL, *next = NULL, *name, *p, buf[256];
|
|
uint32_t interval = 0, rate = 0, bit = 0;
|
|
|
|
switch_copy_string(buf, prefs[x], sizeof(buf));
|
|
last = name = next = cur = buf;
|
|
|
|
for (;;) {
|
|
if (!next) {
|
|
break;
|
|
}
|
|
|
|
if ((p = strchr(next, '@'))) {
|
|
*p++ = '\0';
|
|
}
|
|
next = p;
|
|
|
|
if (cur != name) {
|
|
if (strchr(cur, 'i')) {
|
|
interval = atoi(cur);
|
|
} else if ((strchr(cur, 'k') || strchr(cur, 'h'))) {
|
|
rate = atoi(cur);
|
|
} else if (strchr(cur, 'b')) {
|
|
bit = atoi(cur);
|
|
}
|
|
}
|
|
cur = next;
|
|
}
|
|
|
|
if ((codec_interface = switch_loadable_module_get_codec_interface(name)) != 0) {
|
|
/* If no specific codec interval is requested opt for the default above all else because lots of stuff assumes it */
|
|
for (imp = codec_interface->implementations; imp; imp = imp->next) {
|
|
uint32_t default_ptime = switch_default_ptime(imp->iananame, imp->ianacode);
|
|
|
|
if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) {
|
|
|
|
if ((!interval && (uint32_t) (imp->microseconds_per_packet / 1000) != default_ptime) ||
|
|
(interval && (uint32_t) (imp->microseconds_per_packet / 1000) != interval)) {
|
|
continue;
|
|
}
|
|
|
|
if (((!rate && (uint32_t) imp->samples_per_second != 8000) || (rate && (uint32_t) imp->samples_per_second != rate))) {
|
|
continue;
|
|
}
|
|
|
|
if (bit && (uint32_t) imp->bits_per_second != bit) {
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
array[i++] = imp;
|
|
goto found;
|
|
|
|
}
|
|
|
|
/* Either looking for a specific interval or there was no interval specified and there wasn't one at the default ptime available */
|
|
for (imp = codec_interface->implementations; imp; imp = imp->next) {
|
|
if (imp->codec_type != SWITCH_CODEC_TYPE_VIDEO) {
|
|
|
|
if (interval && (uint32_t) (imp->microseconds_per_packet / 1000) != interval) {
|
|
continue;
|
|
}
|
|
|
|
if (rate && (uint32_t) imp->samples_per_second != rate) {
|
|
continue;
|
|
}
|
|
|
|
if (bit && (uint32_t) imp->bits_per_second != bit) {
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
array[i++] = imp;
|
|
goto found;
|
|
|
|
}
|
|
|
|
found:
|
|
|
|
UNPROTECT_INTERFACE(codec_interface);
|
|
|
|
if (i > arraylen) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
switch_mutex_unlock(loadable_modules.mutex);
|
|
|
|
switch_loadable_module_sort_codecs(array, i);
|
|
|
|
return i;
|
|
}
|
|
|
|
SWITCH_DECLARE(switch_status_t) switch_api_execute(const char *cmd, const char *arg, switch_core_session_t *session, switch_stream_handle_t *stream)
|
|
{
|
|
switch_api_interface_t *api;
|
|
switch_status_t status;
|
|
char *arg_used;
|
|
char *cmd_used;
|
|
|
|
switch_assert(stream != NULL);
|
|
switch_assert(stream->data != NULL);
|
|
switch_assert(stream->write_function != NULL);
|
|
|
|
if (strcasecmp(cmd, "console_complete")) {
|
|
cmd_used = switch_strip_whitespace(cmd);
|
|
arg_used = switch_strip_whitespace(arg);
|
|
} else {
|
|
cmd_used = (char *) cmd;
|
|
arg_used = (char *) arg;
|
|
}
|
|
|
|
|
|
if (!stream->param_event) {
|
|
switch_event_create(&stream->param_event, SWITCH_EVENT_API);
|
|
}
|
|
|
|
if (stream->param_event) {
|
|
if (cmd_used) {
|
|
switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command", cmd_used);
|
|
}
|
|
if (arg_used) {
|
|
switch_event_add_header_string(stream->param_event, SWITCH_STACK_BOTTOM, "API-Command-Argument", arg_used);
|
|
}
|
|
}
|
|
|
|
|
|
if (cmd_used && (api = switch_loadable_module_get_api_interface(cmd_used)) != 0) {
|
|
if ((status = api->function(arg_used, session, stream)) != SWITCH_STATUS_SUCCESS) {
|
|
stream->write_function(stream, "COMMAND RETURNED ERROR!\n");
|
|
}
|
|
UNPROTECT_INTERFACE(api);
|
|
} else {
|
|
status = SWITCH_STATUS_FALSE;
|
|
stream->write_function(stream, "INVALID COMMAND!\n");
|
|
}
|
|
|
|
if (stream->param_event) {
|
|
switch_event_fire(&stream->param_event);
|
|
}
|
|
|
|
if (cmd_used != cmd) {
|
|
switch_safe_free(cmd_used);
|
|
}
|
|
|
|
if (arg_used != arg) {
|
|
switch_safe_free(arg_used);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
SWITCH_DECLARE(switch_loadable_module_interface_t *) switch_loadable_module_create_module_interface(switch_memory_pool_t *pool, const char *name)
|
|
{
|
|
switch_loadable_module_interface_t *mod;
|
|
|
|
mod = switch_core_alloc(pool, sizeof(switch_loadable_module_interface_t));
|
|
switch_assert(mod != NULL);
|
|
|
|
mod->pool = pool;
|
|
|
|
mod->module_name = switch_core_strdup(mod->pool, name);
|
|
switch_thread_rwlock_create(&mod->rwlock, mod->pool);
|
|
return mod;
|
|
}
|
|
|
|
#define ALLOC_INTERFACE(_TYPE_) { \
|
|
switch_##_TYPE_##_interface_t *i, *ptr; \
|
|
i = switch_core_alloc(mod->pool, sizeof(switch_##_TYPE_##_interface_t)); \
|
|
switch_assert(i != NULL); \
|
|
for (ptr = mod->_TYPE_##_interface; ptr && ptr->next; ptr = ptr->next); \
|
|
if (ptr) { \
|
|
ptr->next = i; \
|
|
} else { \
|
|
mod->_TYPE_##_interface = i; \
|
|
} \
|
|
switch_thread_rwlock_create(&i->rwlock, mod->pool); \
|
|
switch_mutex_init(&i->reflock, SWITCH_MUTEX_NESTED, mod->pool); \
|
|
i->parent = mod; \
|
|
return i; }
|
|
|
|
|
|
SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_module_interface_t *mod, switch_module_interface_name_t iname)
|
|
{
|
|
|
|
switch (iname) {
|
|
case SWITCH_ENDPOINT_INTERFACE:
|
|
ALLOC_INTERFACE(endpoint)
|
|
|
|
case SWITCH_TIMER_INTERFACE:
|
|
ALLOC_INTERFACE(timer)
|
|
|
|
case SWITCH_DIALPLAN_INTERFACE:
|
|
ALLOC_INTERFACE(dialplan)
|
|
|
|
case SWITCH_CODEC_INTERFACE:
|
|
ALLOC_INTERFACE(codec)
|
|
|
|
case SWITCH_APPLICATION_INTERFACE:
|
|
ALLOC_INTERFACE(application)
|
|
|
|
case SWITCH_API_INTERFACE:
|
|
ALLOC_INTERFACE(api)
|
|
|
|
case SWITCH_FILE_INTERFACE:
|
|
ALLOC_INTERFACE(file)
|
|
|
|
case SWITCH_SPEECH_INTERFACE:
|
|
ALLOC_INTERFACE(speech)
|
|
|
|
case SWITCH_DIRECTORY_INTERFACE:
|
|
ALLOC_INTERFACE(directory)
|
|
|
|
case SWITCH_CHAT_INTERFACE:
|
|
ALLOC_INTERFACE(chat)
|
|
|
|
case SWITCH_SAY_INTERFACE:
|
|
ALLOC_INTERFACE(say)
|
|
|
|
case SWITCH_ASR_INTERFACE:
|
|
ALLOC_INTERFACE(asr)
|
|
|
|
case SWITCH_MANAGEMENT_INTERFACE:
|
|
ALLOC_INTERFACE(management)
|
|
|
|
case SWITCH_LIMIT_INTERFACE:
|
|
ALLOC_INTERFACE(limit)
|
|
|
|
default:
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Module Type!\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* 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:
|
|
*/
|