sync changes in branch rupa_limit

This commit is contained in:
Rupa Schomaker 2010-04-01 21:31:14 -05:00
parent f6eda2c23b
commit 0e8a26f840
19 changed files with 2165 additions and 1546 deletions

View File

@ -173,6 +173,7 @@ library_include_HEADERS = \
libs/libteletone/src/libteletone_detect.h \
libs/libteletone/src/libteletone_generate.h \
libs/libteletone/src/libteletone.h \
src/include/switch_limit.h \
src/include/switch_odbc.h
nodist_libfreeswitch_la_SOURCES = \
@ -228,6 +229,7 @@ libfreeswitch_la_SOURCES = \
src/switch_config.c \
src/switch_time.c \
src/switch_odbc.c \
src/switch_limit.c \
src/g711.c \
src/switch_pcm.c \
src/switch_profile.c\

View File

@ -9,10 +9,11 @@ applications/mod_enum
#applications/mod_osp
applications/mod_fifo
#applications/mod_curl
applications/mod_db
applications/mod_hash
applications/mod_voicemail
#applications/mod_directory
#applications/mod_lcr
applications/mod_limit
applications/mod_expr
applications/mod_esf
#applications/mod_easyroute

View File

@ -133,6 +133,7 @@
#include "switch_nat.h"
#include "switch_odbc.h"
#include "switch_json.h"
#include "switch_limit.h"
#include <libteletone.h>

127
src/include/switch_limit.h Normal file
View File

@ -0,0 +1,127 @@
/*
* 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):
*
* Rupa Schomaker <rupa@rupa.com>
*
* switch_limit.h - Limit generic implementations
*
*/
/*!
\defgroup limit1 LIMIT code
\ingroup core1
\{
*/
#ifndef _SWITCH_LIMIT_H
#define _SWITCH_LIMIT_H
SWITCH_BEGIN_EXTERN_C
/*!
\brief Initilize the LIMIT Core System
\param pool the memory pool to use for long term allocations
\note Generally called by the core_init
*/
SWITCH_DECLARE(void) switch_limit_init(switch_memory_pool_t *pool);
/*!
\brief Increment resource.
\param backend to use
\param realm
\param resource
\param max - 0 means no limit, just count
\param interval - 0 means no interval
\return true/false - true ok, false over limit
*/
SWITCH_DECLARE(switch_status_t) switch_limit_incr(const char *backend, switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval);
/*!
\brief Release resource.
\param backend to use
\param realm
\param resource
\return true/false - true ok, false over limit
*/
SWITCH_DECLARE(switch_status_t) switch_limit_release(const char *backend, switch_core_session_t *session, const char *realm, const char *resource);
/*!
\brief get usage count for resource
\param backend to use
\param realm
\param resource
\param rcount - output paramter, rate counter
*/
SWITCH_DECLARE(int) switch_limit_usage(const char *backend, const char *realm, const char *resource, uint32_t *rcount);
/*!
\brief reset all usage counters
\param backend to use
*/
SWITCH_DECLARE(switch_status_t) switch_limit_reset(const char *backend);
/*!
\brief fire event for limit usage
\param backend to use
\param realm
\param resource
\param usage
\param rate
\param max
\param ratemax
*/
SWITCH_DECLARE(void) switch_limit_fire_event(const char *backend, const char *realm, const char *resource, uint32_t usage, uint32_t rate, uint32_t max, uint32_t ratemax);
/*!
\brief retrieve arbitrary status information
\param backend to use
\note caller must free returned value
*/
SWITCH_DECLARE(char *) switch_limit_status(const char *backend);
/*! callback to init a backend */
#define SWITCH_LIMIT_INCR(name) static switch_status_t name (switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval)
#define SWITCH_LIMIT_RELEASE(name) static switch_status_t name (switch_core_session_t *session, const char *realm, const char *resource)
#define SWITCH_LIMIT_USAGE(name) static int name (const char *realm, const char *resource, uint32_t *rcount)
#define SWITCH_LIMIT_RESET(name) static switch_status_t name (void)
#define SWITCH_LIMIT_STATUS(name) static char * name (void)
#define LIMIT_IGNORE_TRANSFER_VARIABLE "limit_ignore_transfer"
#define LIMIT_BACKEND_VARIABLE "limit_backend"
#define LIMIT_EVENT_USAGE "limit::usage"
#define LIMIT_DEF_XFER_EXTEN "limit_exceeded"
SWITCH_END_EXTERN_C
#endif
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -81,6 +81,8 @@ SWITCH_BEGIN_EXTERN_C
switch_asr_interface_t *asr_interface;
/*! the table of management interfaces the module has implmented */
switch_management_interface_t *management_interface;
/*! the table of limit interfaces the module has implmented */
switch_limit_interface_t *limit_interface;
switch_thread_rwlock_t *rwlock;
int refs;
switch_memory_pool_t *pool;
@ -204,6 +206,13 @@ SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interfac
*/
SWITCH_DECLARE(switch_management_interface_t *) switch_loadable_module_get_management_interface(const char *relative_oid);
/*!
\brief Retrieve the limit interface by it's registered name
\param name the name of the limit interface
\return the desired limit interface
*/
SWITCH_DECLARE(switch_limit_interface_t *) switch_loadable_module_get_limit_interface(const char *name);
/*!
\brief Retrieve the list of loaded codecs into an array
\param array the array to populate
@ -315,6 +324,18 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void);
break; \
}
#define SWITCH_ADD_LIMIT(limit_int, int_name, incrptr, releaseptr, usageptr, resetptr, statusptr) \
for (;;) { \
limit_int = (switch_limit_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_LIMIT_INTERFACE); \
limit_int->incr = incrptr; \
limit_int->release = releaseptr; \
limit_int->usage = usageptr; \
limit_int->reset = resetptr; \
limit_int->status = statusptr; \
limit_int->interface_name = int_name; \
break; \
}
SWITCH_DECLARE(uint32_t) switch_core_codec_next_id(void);
#define SWITCH_ADD_CODEC(codec_int, int_name) \

View File

@ -512,6 +512,28 @@ struct switch_management_interface {
struct switch_management_interface *next;
};
/*! \brief Abstract interface to a limit module */
struct switch_limit_interface {
/*! name of the interface */
const char *interface_name;
/*! increment */
switch_status_t (*incr) (switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval);
/*! release */
switch_status_t (*release) (switch_core_session_t *session, const char *realm, const char *resource);
/*! usage for resource */
int (*usage) (const char *realm, const char *resource, uint32_t *rcount);
/*! reset counters */
switch_status_t (*reset) (void);
/*! freform status */
char * (*status) (void);
/* internal */
switch_thread_rwlock_t *rwlock;
int refs;
switch_mutex_t *reflock;
switch_loadable_module_interface_t *parent;
struct switch_limit_interface *next;
};
/*! \brief Abstract interface to a directory module */
struct switch_directory_interface {
/*! the name of the interface */

View File

@ -267,7 +267,8 @@ typedef enum {
SWITCH_CHAT_INTERFACE,
SWITCH_SAY_INTERFACE,
SWITCH_ASR_INTERFACE,
SWITCH_MANAGEMENT_INTERFACE
SWITCH_MANAGEMENT_INTERFACE,
SWITCH_LIMIT_INTERFACE
} switch_module_interface_name_t;
typedef enum {
@ -1593,6 +1594,7 @@ typedef struct switch_chat_interface switch_chat_interface_t;
typedef struct switch_management_interface switch_management_interface_t;
typedef struct switch_core_port_allocator switch_core_port_allocator_t;
typedef struct switch_media_bug switch_media_bug_t;
typedef struct switch_limit_interface switch_limit_interface_t;
struct switch_console_callback_match_node {
char *val;

View File

@ -3358,7 +3358,7 @@ SWITCH_STANDARD_API(alias_function)
return SWITCH_STATUS_SUCCESS;
}
#define SHOW_SYNTAX "codec|endpoint|application|api|dialplan|file|timer|calls [count]|channels [count|like <match string>]|distinct_channels|aliases|complete|chat|management|modules|nat_map|say|interfaces|interface_types|tasks"
#define SHOW_SYNTAX "codec|endpoint|application|api|dialplan|file|timer|calls [count]|channels [count|like <match string>]|distinct_channels|aliases|complete|chat|management|modules|nat_map|say|interfaces|interface_types|tasks|limits"
SWITCH_STANDARD_API(show_function)
{
char sql[1024];
@ -3421,6 +3421,7 @@ SWITCH_STANDARD_API(show_function)
!strncasecmp(command, "file", 4) ||
!strncasecmp(command, "timer", 5) ||
!strncasecmp(command, "chat", 4) ||
!strncasecmp(command, "limit", 5) ||
!strncasecmp(command, "say", 3) || !strncasecmp(command, "management", 10) || !strncasecmp(command, "endpoint", 8)) {
if (end_of(command) == 's') {
end_of(command) = '\0';
@ -4222,6 +4223,107 @@ SWITCH_STANDARD_API(sql_escape)
return SWITCH_STATUS_SUCCESS;
}
/* LIMIT Stuff */
#define LIMIT_USAGE_USAGE "<backend> <realm> <id> [rate]"
SWITCH_STANDARD_API(limit_usage_function)
{
int argc = 0;
char *argv[5] = { 0 };
char *mydata = NULL;
uint32_t count = 0;
uint32_t rcount = 0;
switch_bool_t dorate = SWITCH_FALSE;
if (!zstr(cmd)) {
mydata = strdup(cmd);
switch_assert(mydata);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 3) {
stream->write_function(stream, "USAGE: limit_usage %s\n", LIMIT_USAGE_USAGE);
goto end;
}
if (argc > 3) {
if (!strcasecmp("rate", argv[3])) {
dorate = SWITCH_TRUE;
}
}
count = switch_limit_usage(argv[0], argv[1], argv[2], &rcount);
if (dorate == SWITCH_TRUE) {
stream->write_function(stream, "%d/%d", count, rcount);
} else {
stream->write_function(stream, "%d", count);
}
end:
switch_safe_free(mydata);
return SWITCH_STATUS_SUCCESS;
}
#define LIMIT_STATUS_USAGE "<backend>"
SWITCH_STANDARD_API(limit_status_function)
{
int argc = 0;
char *argv[2] = { 0 };
char *mydata = NULL;
char *ret = NULL;
if (!zstr(cmd)) {
mydata = strdup(cmd);
switch_assert(mydata);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 1) {
stream->write_function(stream, "USAGE: limit_status %s\n", LIMIT_STATUS_USAGE);
goto end;
}
ret = switch_limit_status(argv[0]);
stream->write_function(stream, "%s", ret);
end:
switch_safe_free(mydata);
switch_safe_free(ret);
return SWITCH_STATUS_SUCCESS;
}
#define LIMIT_RESET_USAGE "<backend>"
SWITCH_STANDARD_API(limit_reset_function)
{
int argc = 0;
char *argv[2] = { 0 };
char *mydata = NULL;
switch_status_t ret = SWITCH_STATUS_SUCCESS;
if (!zstr(cmd)) {
mydata = strdup(cmd);
switch_assert(mydata);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 1) {
stream->write_function(stream, "USAGE: limit_reset %s\n", LIMIT_RESET_USAGE);
goto end;
}
ret = switch_limit_reset(argv[0]);
stream->write_function(stream, "%s", (ret == SWITCH_STATUS_SUCCESS) ? "+OK" : "-ERR");
end:
switch_safe_free(mydata);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_commands_shutdown)
{
int x;
@ -4310,6 +4412,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
SWITCH_ADD_API(commands_api_interface, "hupall", "hupall", hupall_api_function, "<cause> [<var> <value>]");
SWITCH_ADD_API(commands_api_interface, "in_group", "determine if a user is in a group", in_group_function, "<user>[@<domain>] <group_name>");
SWITCH_ADD_API(commands_api_interface, "is_lan_addr", "see if an ip is a lan addr", lan_addr_function, "<ip>");
SWITCH_ADD_API(commands_api_interface, "limit_usage", "Gets the usage count of a limited resource", limit_usage_function, "<backend> <realm> <id>");
SWITCH_ADD_API(commands_api_interface, "limit_status", "Gets the status of a limit backend", limit_status_function, "<backend>");
SWITCH_ADD_API(commands_api_interface, "limit_reset", "Reset the counters of a limit backend", limit_reset_function, "<backend>");
SWITCH_ADD_API(commands_api_interface, "load", "Load Module", load_function, LOAD_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "log", "Log", log_function, LOG_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "md5", "md5", md5_function, "<data>");

View File

@ -2,9 +2,9 @@
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="mod_limit"
Name="mod_db"
ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
RootNamespace="mod_limit"
RootNamespace="mod_db"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
@ -274,7 +274,7 @@
</References>
<Files>
<File
RelativePath=".\mod_limit.c"
RelativePath=".\mod_db.c"
>
</File>
</Files>

View File

@ -0,0 +1,663 @@
/*
* 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>
* Ken Rice <krice at suspicious dot org
* Mathieu Rene <mathieu.rene@gmail.com>
* Bret McDanel <trixter AT 0xdecafbad.com>
* Rupa Schomaker <rupa@rupa.com>
*
* mod_db.c -- Implements simple db API, group support, and limit db backend
*
*/
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_db_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_db_shutdown);
SWITCH_MODULE_DEFINITION(mod_db, mod_db_load, mod_db_shutdown, NULL);
static struct {
switch_memory_pool_t *pool;
char hostname[256];
char *dbname;
char *odbc_dsn;
char *odbc_user;
char *odbc_pass;
switch_mutex_t *mutex;
switch_mutex_t *db_hash_mutex;
switch_hash_t *db_hash;
} globals;
typedef struct {
uint32_t total_usage;
uint32_t rate_usage;
time_t last_check;
} limit_hash_item_t;
struct callback {
char *buf;
size_t len;
int matches;
};
typedef struct callback callback_t;
static int sql2str_callback(void *pArg, int argc, char **argv, char **columnNames)
{
callback_t *cbt = (callback_t *) pArg;
switch_copy_string(cbt->buf, argv[0], cbt->len);
cbt->matches++;
return 0;
}
static char limit_sql[] =
"CREATE TABLE limit_data (\n"
" hostname VARCHAR(255),\n" " realm VARCHAR(255),\n" " id VARCHAR(255),\n" " uuid VARCHAR(255)\n" ");\n";
static char db_sql[] =
"CREATE TABLE db_data (\n"
" hostname VARCHAR(255),\n" " realm VARCHAR(255),\n" " data_key VARCHAR(255),\n" " data VARCHAR(255)\n" ");\n";
static char group_sql[] =
"CREATE TABLE group_data (\n" " hostname VARCHAR(255),\n" " groupname VARCHAR(255),\n" " url VARCHAR(255)\n" ");\n";
switch_cache_db_handle_t *limit_get_db_handle(void)
{
switch_cache_db_connection_options_t options = { {0} };
switch_cache_db_handle_t *dbh = NULL;
if (!zstr(globals.odbc_dsn)) {
options.odbc_options.dsn = globals.odbc_dsn;
options.odbc_options.user = globals.odbc_user;
options.odbc_options.pass = globals.odbc_pass;
if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_ODBC, &options) != SWITCH_STATUS_SUCCESS)
dbh = NULL;
return dbh;
} else {
options.core_db_options.db_path = globals.dbname;
if (switch_cache_db_get_db_handle(&dbh, SCDB_TYPE_CORE_DB, &options) != SWITCH_STATUS_SUCCESS)
dbh = NULL;
return dbh;
}
}
static switch_status_t limit_execute_sql(char *sql)
{
switch_cache_db_handle_t *dbh = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
if (!(dbh = limit_get_db_handle())) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
goto end;
}
status = switch_cache_db_execute_sql(dbh, sql, NULL);
end:
switch_cache_db_release_db_handle(&dbh);
return status;
}
static switch_bool_t limit_execute_sql_callback(char *sql, switch_core_db_callback_func_t callback, void *pdata)
{
switch_bool_t ret = SWITCH_FALSE;
char *errmsg = NULL;
switch_cache_db_handle_t *dbh = NULL;
if (!(dbh = limit_get_db_handle())) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB\n");
goto end;
}
switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, &errmsg);
if (errmsg) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
free(errmsg);
}
end:
switch_cache_db_release_db_handle(&dbh);
return ret;
}
static char * limit_execute_sql2str(char *sql, char *str, size_t len)
{
callback_t cbt = { 0 };
cbt.buf = str;
cbt.len = len;
limit_execute_sql_callback(sql, sql2str_callback, &cbt);
return cbt.buf;
}
/* \brief Enforces limit restrictions
* \param session current session
* \param realm limit realm
* \param id limit id
* \param max maximum count
* \return SWITCH_STATUS_SUCCESS if the access is allowed
*/
SWITCH_LIMIT_INCR(limit_incr_sql)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
int got = 0;
char *sql = NULL;
char gotstr[128];
switch_status_t status = SWITCH_STATUS_SUCCESS;
// check max! WTF
switch_mutex_lock(globals.mutex);
switch_channel_set_variable(channel, "limit_realm", realm);
switch_channel_set_variable(channel, "limit_id", resource);
switch_channel_set_variable(channel, "limit_max", switch_core_session_sprintf(session, "%d", max));
sql = switch_mprintf("select count(hostname) from limit_data where realm='%q' and id='%q';", realm, resource);
limit_execute_sql2str(sql, gotstr, 128);
switch_safe_free(sql);
got = atoi(gotstr);
if (max < 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s_%s is now %d\n", realm, resource, got + 1);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s_%s is now %d/%d\n", realm, resource, got + 1, max);
}
if (max >= 0 && got + 1 > max) {
status = SWITCH_STATUS_GENERR;
goto done;
}
sql =
switch_mprintf("insert into limit_data (hostname, realm, id, uuid) values('%q','%q','%q','%q');", globals.hostname, realm, resource,
switch_core_session_get_uuid(session));
limit_execute_sql(sql);
switch_safe_free(sql);
{
const char *susage = switch_core_session_sprintf(session, "%d", ++got);
switch_channel_set_variable(channel, "limit_usage", susage);
switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s_%s", realm, resource), susage);
}
switch_limit_fire_event("sql", realm, resource, got, 0, max, 0);
done:
switch_mutex_unlock(globals.mutex);
return status;
}
SWITCH_LIMIT_RELEASE(limit_release_sql)
{
char *sql = NULL;
if (realm == NULL && resource == NULL) {
sql = switch_mprintf("delete from limit_data where uuid='%q'", switch_core_session_get_uuid(session));
} else {
sql = switch_mprintf("delete from limit_data where uuid='%q' and realm='%q' and id = '%q'", switch_core_session_get_uuid(session), realm, resource);
}
limit_execute_sql(sql);
switch_safe_free(sql);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_LIMIT_USAGE(limit_usage_sql)
{
char usagestr[128] = "";
int usage = 0;
char *sql = NULL;
sql = switch_mprintf("select count(hostname) from limit_data where realm='%q' and id='%q'", realm, resource);
limit_execute_sql2str(sql, usagestr, sizeof(usagestr));
switch_safe_free(sql);
usage = atoi(usagestr);
return usage;
}
SWITCH_LIMIT_RESET(limit_reset_sql)
{
char *sql = NULL;
sql = switch_mprintf("delete from limit_data where hostname='%q';", globals.hostname);
limit_execute_sql(sql);
switch_safe_free(sql);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_LIMIT_STATUS(limit_status_sql)
{
char count[128] = "";
char *ret = NULL;
char *sql = NULL;
sql = switch_mprintf("select count(hostname) from limit_data where hostname='%q'", globals.hostname);
limit_execute_sql2str(sql, count, sizeof(count));
switch_safe_free(sql);
ret = switch_mprintf("Tracking %s resources for hostname %s.", count, globals.hostname);
return ret;
}
/* INIT / Config */
static switch_xml_config_string_options_t limit_config_dsn = { NULL, 0, "[^:]+:[^:]+:.+" };
static switch_xml_config_item_t config_settings[] = {
SWITCH_CONFIG_ITEM("odbc-dsn", SWITCH_CONFIG_STRING, 0, &globals.odbc_dsn, NULL, &limit_config_dsn,
"dsn:username:password", "If set, the ODBC DSN used by the limit and db applications"),
SWITCH_CONFIG_ITEM_END()
};
static switch_status_t do_config()
{
switch_cache_db_handle_t *dbh = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
char *sql = NULL;
limit_config_dsn.pool = globals.pool;
if (switch_xml_config_parse_module_settings("db.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_TERM;
}
if (globals.odbc_dsn) {
if ((globals.odbc_user = strchr(globals.odbc_dsn, ':'))) {
*globals.odbc_user++ = '\0';
if ((globals.odbc_pass = strchr(globals.odbc_user, ':'))) {
*globals.odbc_pass++ = '\0';
}
}
if (!(dbh = limit_get_db_handle())) {
globals.odbc_dsn = globals.odbc_user = globals.odbc_pass;
}
}
if (zstr(globals.odbc_dsn)) {
globals.dbname = "call_limit";
dbh = limit_get_db_handle();
}
if (dbh) {
int x = 0;
char *indexes[] = {
"create index ld_hostname on limit_data (hostname)",
"create index ld_uuid on limit_data (uuid)",
"create index ld_realm on limit_data (realm)",
"create index ld_id on limit_data (id)",
"create index dd_realm on db_data (realm)",
"create index dd_data_key on db_data (data_key)",
"create index gd_groupname on group_data (groupname)",
"create index gd_url on group_data (url)",
NULL
};
switch_cache_db_test_reactive(dbh, "select * from limit_data", NULL, limit_sql);
switch_cache_db_test_reactive(dbh, "select * from db_data", NULL, db_sql);
switch_cache_db_test_reactive(dbh, "select * from group_data", NULL, group_sql);
for (x = 0; indexes[x]; x++) {
switch_cache_db_execute_sql(dbh, indexes[x], NULL);
}
switch_cache_db_release_db_handle(&dbh);
sql = switch_mprintf("delete from limit_data where hostname='%q';", globals.hostname);
limit_execute_sql(sql);
switch_safe_free(sql);
}
return status;
}
/* APP/API STUFF */
/* CORE DB STUFF */
SWITCH_STANDARD_API(db_api_function)
{
int argc = 0;
char *argv[4] = { 0 };
char *mydata = NULL;
char *sql;
if (!zstr(cmd)) {
mydata = strdup(cmd);
switch_assert(mydata);
argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 1 || !argv[0]) {
goto error;
}
if (!strcasecmp(argv[0], "insert")) {
if (argc < 4) {
goto error;
}
sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
sql =
switch_mprintf("insert into db_data (hostname, realm, data_key, data) values('%q','%q','%q','%q');", globals.hostname, argv[1], argv[2],
argv[3]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
stream->write_function(stream, "+OK");
goto done;
} else if (!strcasecmp(argv[0], "delete")) {
if (argc < 2) {
goto error;
}
sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
stream->write_function(stream, "+OK");
goto done;
} else if (!strcasecmp(argv[0], "select")) {
char buf[256] = "";
if (argc < 3) {
goto error;
}
sql = switch_mprintf("select data from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
limit_execute_sql2str(sql, buf, sizeof(buf));
switch_safe_free(sql);
stream->write_function(stream, "%s", buf);
goto done;
}
error:
stream->write_function(stream, "!err!");
done:
switch_safe_free(mydata);
return SWITCH_STATUS_SUCCESS;
}
#define DB_USAGE "[insert|delete]/<realm>/<key>/<val>"
#define DB_DESC "save data"
SWITCH_STANDARD_APP(db_function)
{
int argc = 0;
char *argv[4] = { 0 };
char *mydata = NULL;
char *sql = NULL;
if (!zstr(data)) {
mydata = switch_core_session_strdup(session, data);
argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 3 || !argv[0]) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
return;
}
if (!strcasecmp(argv[0], "insert")) {
if (argc < 4) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
return;
}
sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
sql =
switch_mprintf("insert into db_data (hostname, realm, data_key, data) values('%q','%q','%q','%q');", globals.hostname, argv[1], argv[2],
argv[3]);
} else if (!strcasecmp(argv[0], "delete")) {
sql = switch_mprintf("delete from db_data where realm='%q' and data_key='%q'", argv[1], argv[2]);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: db %s\n", DB_USAGE);
return;
}
if (sql) {
limit_execute_sql(sql);
switch_safe_free(sql);
}
}
/* GROUP STUFF */
static int group_callback(void *pArg, int argc, char **argv, char **columnNames)
{
callback_t *cbt = (callback_t *) pArg;
switch_snprintf(cbt->buf + strlen(cbt->buf), cbt->len - strlen(cbt->buf), "%s%c", argv[0], *argv[1]);
cbt->matches++;
return 0;
}
SWITCH_STANDARD_API(group_api_function)
{
int argc = 0;
char *argv[4] = { 0 };
char *mydata = NULL;
char *sql;
if (!zstr(cmd)) {
mydata = strdup(cmd);
argc = switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 2 || !argv[0]) {
goto error;
}
if (!strcasecmp(argv[0], "insert")) {
if (argc < 3) {
goto error;
}
sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
sql = switch_mprintf("insert into group_data (hostname, groupname, url) values('%q','%q','%q');", globals.hostname, argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
stream->write_function(stream, "+OK");
goto done;
} else if (!strcasecmp(argv[0], "delete")) {
if (argc < 3) {
goto error;
}
if (!strcmp(argv[2], "*")) {
sql = switch_mprintf("delete from group_data where groupname='%q';", argv[1]);
} else {
sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
}
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
stream->write_function(stream, "+OK");
goto done;
} else if (!strcasecmp(argv[0], "call")) {
char buf[4096] = "";
char *how = ",";
callback_t cbt = { 0 };
cbt.buf = buf;
cbt.len = sizeof(buf);
if (argc > 2) {
if (!strcasecmp(argv[2], "order")) {
how = "|";
}
}
sql = switch_mprintf("select url,'%q' from group_data where groupname='%q'", how, argv[1]);
switch_assert(sql);
limit_execute_sql_callback(sql, group_callback, &cbt);
switch_safe_free(sql);
*(buf + (strlen(buf) - 1)) = '\0';
stream->write_function(stream, "%s", buf);
goto done;
}
error:
stream->write_function(stream, "!err!");
done:
switch_safe_free(mydata);
return SWITCH_STATUS_SUCCESS;
}
#define GROUP_USAGE "[insert|delete]:<group name>:<val>"
#define GROUP_DESC "save data"
SWITCH_STANDARD_APP(group_function)
{
int argc = 0;
char *argv[3] = { 0 };
char *mydata = NULL;
char *sql;
if (!zstr(data)) {
mydata = switch_core_session_strdup(session, data);
argc = switch_separate_string(mydata, ':', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 3 || !argv[0]) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: group %s\n", DB_USAGE);
return;
}
if (!strcasecmp(argv[0], "insert")) {
sql = switch_mprintf("insert into group_data (hostname, groupname, url) values('%q','%q','%q');", globals.hostname, argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
} else if (!strcasecmp(argv[0], "delete")) {
sql = switch_mprintf("delete from group_data where groupname='%q' and url='%q';", argv[1], argv[2]);
switch_assert(sql);
limit_execute_sql(sql);
switch_safe_free(sql);
}
}
/* INIT/DEINIT STUFF */
SWITCH_MODULE_LOAD_FUNCTION(mod_db_load)
{
switch_status_t status;
switch_application_interface_t *app_interface;
switch_api_interface_t *commands_api_interface;
switch_limit_interface_t *limit_interface;
memset(&globals, 0, sizeof(&globals));
gethostname(globals.hostname, sizeof(globals.hostname));
globals.pool = pool;
if ((status = do_config() != SWITCH_STATUS_SUCCESS)) {
return status;
}
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
switch_mutex_init(&globals.db_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
switch_core_hash_init(&globals.db_hash, pool);
status = switch_event_reserve_subclass(LIMIT_EVENT_USAGE);
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_INUSE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register event subclass \"%s\" (%d)\n", LIMIT_EVENT_USAGE, status);
return SWITCH_STATUS_FALSE;
}
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
/* register limit interfaces */
SWITCH_ADD_LIMIT(limit_interface, "sql", limit_incr_sql, limit_release_sql, limit_usage_sql, limit_reset_sql, limit_status_sql);
SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
switch_console_set_complete("add db insert");
switch_console_set_complete("add db delete");
switch_console_set_complete("add db select");
SWITCH_ADD_API(commands_api_interface, "group", "group [insert|delete|call]", group_api_function, "[insert|delete|call]:<group name>:<url>");
switch_console_set_complete("add group insert");
switch_console_set_complete("add group delete");
switch_console_set_complete("add group call");
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_db_shutdown)
{
switch_xml_config_cleanup(config_settings);
switch_mutex_destroy(globals.mutex);
switch_mutex_destroy(globals.db_hash_mutex);
switch_core_hash_destroy(&globals.db_hash);
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -3007,6 +3007,140 @@ SWITCH_STANDARD_APP(session_loglevel_function)
}
}
/* LIMIT STUFF */
#define LIMIT_USAGE "<backend> <realm> <id> [<max>[/interval]] [number [dialplan [context]]]"
#define LIMIT_DESC "limit access to a resource and transfer to an extension if the limit is exceeded"
SWITCH_STANDARD_APP(limit_function)
{
int argc = 0;
char *argv[7] = { 0 };
char *mydata = NULL;
char *backend = NULL;
char *realm = NULL;
char *id = NULL;
char *xfer_exten = NULL;
int max = -1;
int interval = 0;
switch_channel_t *channel = switch_core_session_get_channel(session);
/* Parse application data */
if (!zstr(data)) {
mydata = switch_core_session_strdup(session, data);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 3) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit %s\n", LIMIT_USAGE);
return;
}
backend = argv[0];
realm = argv[1];
id = argv[2];
/* If max is omitted or negative, only act as a counter and skip maximum checks */
if (argc > 3) {
if (argv[3][0] == '-') {
max = -1;
} else {
char *szinterval = NULL;
if ((szinterval = strchr(argv[3], '/'))) {
*szinterval++ = '\0';
interval = atoi(szinterval);
}
max = atoi(argv[3]);
if (max < 0) {
max = 0;
}
}
}
if (argc > 4) {
xfer_exten = argv[4];
} else {
xfer_exten = LIMIT_DEF_XFER_EXTEN;
}
if (switch_limit_incr(backend, session, realm, id, max, interval) != SWITCH_STATUS_SUCCESS) {
/* Limit exceeded */
if (*xfer_exten == '!') {
switch_channel_hangup(channel, switch_channel_str2cause(xfer_exten + 1));
} else {
switch_ivr_session_transfer(session, xfer_exten, argv[5], argv[6]);
}
}
}
#define LIMITEXECUTE_USAGE "<backend> <realm> <id> [<max>[/interval]] [application] [application arguments]"
#define LIMITEXECUTE_DESC "limit access to a resource. the specified application will only be executed if the resource is available"
SWITCH_STANDARD_APP(limit_execute_function)
{
int argc = 0;
char *argv[6] = { 0 };
char *mydata = NULL;
char *backend = NULL;
char *realm = NULL;
char *id = NULL;
char *app = NULL;
char *app_arg = NULL;
int max = -1;
int interval = 0;
/* Parse application data */
if (!zstr(data)) {
mydata = switch_core_session_strdup(session, data);
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 6) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: limit_execute %s\n", LIMITEXECUTE_USAGE);
return;
}
backend = argv[0];
realm = argv[1];
id = argv[2];
/* Accept '-' as unlimited (act as counter) */
if (argv[3][0] == '-') {
max = -1;
} else {
char *szinterval = NULL;
if ((szinterval = strchr(argv[3], '/'))) {
*szinterval++ = '\0';
interval = atoi(szinterval);
}
max = atoi(argv[3]);
if (max < 0) {
max = 0;
}
}
app = argv[4];
app_arg = argv[5];
if (zstr(app)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Missing application\n");
return;
}
if (switch_limit_incr(backend, session, realm, id, max, interval) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Executing\n");
switch_core_session_execute_application(session, app, app_arg);
/* Only release the resource if we are still in CS_EXECUTE */
if (switch_channel_get_state(switch_core_session_get_channel(session)) == CS_EXECUTE) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "immediately releasing\n");
switch_limit_release(backend, session, realm, id);
}
}
}
#define SPEAK_DESC "Speak text to a channel via the tts interface"
#define DISPLACE_DESC "Displace audio from a file to the channels input"
#define SESS_REC_DESC "Starts a background recording of the entire session"
@ -3176,6 +3310,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
SAF_NONE);
SWITCH_ADD_APP(app_interface, "session_loglevel", "session_loglevel", "session_loglevel", session_loglevel_function, SESSION_LOGLEVEL_SYNTAX,
SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "limit", "Limit", LIMIT_DESC, limit_function, LIMIT_USAGE, SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "limit_execute", "Limit", LIMITEXECUTE_USAGE, limit_execute_function, LIMITEXECUTE_USAGE, SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_DIALPLAN(dp_interface, "inline", inline_dialplan_hunt);

View File

@ -0,0 +1,283 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="mod_hash"
ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
RootNamespace="mod_hash"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
<Platform
Name="x64"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="17"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="17"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath=".\mod_hash.c"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,506 @@
/*
* 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>
* Ken Rice <krice at suspicious dot org
* Mathieu Rene <mathieu.rene@gmail.com>
* Bret McDanel <trixter AT 0xdecafbad.com>
* Rupa Schomaker <rupa@rupa.com>
*
* mod_hash.c -- Hash api, hash backend for limit
*
*/
#include <switch.h>
#define LIMIT_HASH_CLEANUP_INTERVAL 900
SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hash_shutdown);
SWITCH_MODULE_DEFINITION(mod_hash, mod_hash_load, mod_hash_shutdown, NULL);
/* CORE STUFF */
static struct {
switch_memory_pool_t *pool;
switch_thread_rwlock_t *limit_hash_rwlock;
switch_hash_t *limit_hash;
switch_thread_rwlock_t *db_hash_rwlock;
switch_hash_t *db_hash;
} globals;
typedef struct {
uint32_t total_usage;
uint32_t rate_usage;
time_t last_check;
uint32_t interval;
} limit_hash_item_t;
struct callback {
char *buf;
size_t len;
int matches;
};
typedef struct callback callback_t;
/* HASH STUFF */
typedef struct {
switch_hash_t *hash;
} limit_hash_private_t;
/* \brief Enforces limit_hash restrictions
* \param session current session
* \param realm limit realm
* \param id limit id
* \param max maximum count
* \param interval interval for rate limiting
* \return SWITCH_TRUE if the access is allowed, SWITCH_FALSE if it isnt
*/
SWITCH_LIMIT_INCR(limit_incr_hash)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
char *hashkey = NULL;
switch_bool_t status = SWITCH_STATUS_SUCCESS;
limit_hash_item_t *item = NULL;
time_t now = switch_epoch_time_now(NULL);
limit_hash_private_t *pvt = NULL;
uint8_t increment = 1;
hashkey = switch_core_session_sprintf(session, "%s_%s", realm, resource);
switch_thread_rwlock_wrlock(globals.limit_hash_rwlock);
/* Check if that realm+resource has ever been checked */
if (!(item = (limit_hash_item_t *) switch_core_hash_find(globals.limit_hash, hashkey))) {
/* No, create an empty structure and add it, then continue like as if it existed */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Creating new limit structure: key: %s\n", hashkey);
item = (limit_hash_item_t *) malloc(sizeof(limit_hash_item_t));
switch_assert(item);
memset(item, 0, sizeof(limit_hash_item_t));
switch_core_hash_insert(globals.limit_hash, hashkey, item);
}
/* Did we already run on this channel before? */
if ((pvt = switch_channel_get_private(channel, "limit_hash"))) {
/* Yes, but check if we did that realm+resource
If we didnt, allow incrementing the counter.
If we did, dont touch it but do the validation anyways
*/
increment = !switch_core_hash_find(pvt->hash, hashkey);
} else {
/* This is the first limit check on this channel, create a hashtable, set our prviate data */
pvt = (limit_hash_private_t *) switch_core_session_alloc(session, sizeof(limit_hash_private_t));
memset(pvt, 0, sizeof(limit_hash_private_t));
switch_core_hash_init(&pvt->hash, switch_core_session_get_pool(session));
switch_channel_set_private(channel, "limit_hash", pvt);
}
if (interval > 0) {
item->interval = interval;
if (item->last_check <= (now - interval)) {
item->rate_usage = 1;
item->last_check = now;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Usage for %s reset to 1\n",
hashkey);
} else {
/* Always increment rate when its checked as it doesnt depend on the channel */
item->rate_usage++;
if ((max >= 0) && (item->rate_usage > (uint32_t) max)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s exceeds maximum rate of %d/%ds, now at %d\n",
hashkey, max, interval, item->rate_usage);
status = SWITCH_STATUS_GENERR;
goto end;
}
}
} else if ((max >= 0) && (item->total_usage + increment > (uint32_t) max)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is already at max value (%d)\n", hashkey, item->total_usage);
status = SWITCH_STATUS_GENERR;
goto end;
}
if (increment) {
item->total_usage++;
switch_core_hash_insert(pvt->hash, hashkey, item);
if (max == -1) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", hashkey, item->total_usage);
} else if (interval == 0) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d/%d\n", hashkey, item->total_usage, max);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d/%d for the last %d seconds\n", hashkey,
item->rate_usage, max, interval);
}
switch_limit_fire_event("hash", realm, resource, item->total_usage, item->rate_usage, max, max >= 0 ? (uint32_t) max : 0);
}
/* Save current usage & rate into channel variables so it can be used later in the dialplan, or added to CDR records */
{
const char *susage = switch_core_session_sprintf(session, "%d", item->total_usage);
const char *srate = switch_core_session_sprintf(session, "%d", item->rate_usage);
switch_channel_set_variable(channel, "limit_usage", susage);
switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_usage_%s", hashkey), susage);
switch_channel_set_variable(channel, "limit_rate", srate);
switch_channel_set_variable(channel, switch_core_session_sprintf(session, "limit_rate_%s", hashkey), srate);
}
end:
switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
return status;
}
/* !\brief Determines whether a given entry is ready to be removed. */
SWITCH_HASH_DELETE_FUNC(limit_hash_cleanup_delete_callback) {
limit_hash_item_t *item = (limit_hash_item_t *) val;
time_t now = switch_epoch_time_now(NULL);
/* reset to 0 if window has passed so we can clean it up */
if (item->rate_usage > 0 && (item->last_check <= (now - item->interval))) {
item->rate_usage = 0;
}
if (item->total_usage == 0 && item->rate_usage == 0) {
/* Noone is using this item anymore */
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Freeing limit item: %s\n", (const char *) key);
free(item);
return SWITCH_TRUE;
}
return SWITCH_FALSE;
}
/* !\brief Periodically checks for unused limit entries and frees them */
SWITCH_STANDARD_SCHED_FUNC(limit_hash_cleanup_callback)
{
switch_thread_rwlock_wrlock(globals.limit_hash_rwlock);
switch_core_hash_delete_multi(globals.limit_hash, limit_hash_cleanup_delete_callback, NULL);
switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
task->runtime = switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL;
}
/* !\brief Releases usage of a limit_hash-controlled ressource */
SWITCH_LIMIT_RELEASE(limit_release_hash)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
limit_hash_private_t *pvt = switch_channel_get_private(channel, "limit_hash");
limit_hash_item_t *item = NULL;
switch_hash_index_t *hi;
char *hashkey = NULL;
if (!pvt || !pvt->hash) {
return SWITCH_STATUS_SUCCESS;
}
switch_thread_rwlock_wrlock(globals.limit_hash_rwlock);
/* clear for uuid */
if (realm == NULL && resource == NULL) {
/* Loop through the channel's hashtable which contains mapping to all the limit_hash_item_t referenced by that channel */
while ((hi = switch_hash_first(NULL, pvt->hash))) {
void *val = NULL;
const void *key;
switch_ssize_t keylen;
limit_hash_item_t *item = NULL;
switch_hash_this(hi, &key, &keylen, &val);
item = (limit_hash_item_t *) val;
item->total_usage--;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", (const char *) key, item->total_usage);
if (item->total_usage == 0 && item->rate_usage == 0) {
/* Noone is using this item anymore */
switch_core_hash_delete(globals.limit_hash, (const char *) key);
free(item);
}
switch_core_hash_delete(pvt->hash, (const char *) key);
}
} else {
hashkey = switch_core_session_sprintf(session, "%s_%s", realm, resource);
if ((item = (limit_hash_item_t *) switch_core_hash_find(pvt->hash, hashkey))) {
item->total_usage--;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s is now %d\n", (const char *) hashkey, item->total_usage);
switch_core_hash_delete(pvt->hash, hashkey);
if (item->total_usage == 0 && item->rate_usage == 0) {
/* Noone is using this item anymore */
switch_core_hash_delete(globals.limit_hash, (const char *) hashkey);
free(item);
}
}
}
switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_LIMIT_USAGE(limit_usage_hash)
{
char *hash_key = NULL;
limit_hash_item_t *item = NULL;
int count = 0;
switch_thread_rwlock_rdlock(globals.limit_hash_rwlock);
hash_key = switch_mprintf("%s_%s", realm, resource);
if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) {
count = item->total_usage;
*rcount = item->rate_usage;
}
switch_safe_free(hash_key);
switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
return count;
}
SWITCH_LIMIT_RESET(limit_reset_hash)
{
return SWITCH_STATUS_GENERR;
}
SWITCH_LIMIT_STATUS(limit_status_hash)
{
/*
switch_hash_index_t *hi = NULL;
int count = 0;
char *ret = NULL;
switch_thread_rwlock_rdlock(globals.limit_hash_rwlock);
for (hi = switch_hash_first(NULL, globals.limit_hash); hi; switch_hash_next(hi)) {
count++;
}
switch_thread_rwlock_unlock(globals.limit_hash_rwlock);
ret = switch_mprintf("There are %d elements being tracked.", count);
return ret;
*/
return strdup("-ERR not supported yet (locking problems).");
}
/* APP/API STUFF */
/* CORE HASH STUFF */
#define HASH_USAGE "[insert|delete]/<realm>/<key>/<val>"
#define HASH_DESC "save data"
SWITCH_STANDARD_APP(hash_function)
{
int argc = 0;
char *argv[4] = { 0 };
char *mydata = NULL;
char *hash_key = NULL;
char *value = NULL;
switch_thread_rwlock_wrlock(globals.db_hash_rwlock);
if (!zstr(data)) {
mydata = strdup(data);
switch_assert(mydata);
argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 3 || !argv[0]) {
goto usage;
}
hash_key = switch_mprintf("%s_%s", argv[1], argv[2]);
if (!strcasecmp(argv[0], "insert")) {
if (argc < 4) {
goto usage;
}
if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
free(value);
switch_core_hash_delete(globals.db_hash, hash_key);
}
value = strdup(argv[3]);
switch_assert(value);
switch_core_hash_insert(globals.db_hash, hash_key, value);
} else if (!strcasecmp(argv[0], "delete")) {
if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
switch_safe_free(value);
switch_core_hash_delete(globals.db_hash, hash_key);
}
} else {
goto usage;
}
goto done;
usage:
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "USAGE: hash %s\n", HASH_USAGE);
done:
switch_thread_rwlock_unlock(globals.db_hash_rwlock);
switch_safe_free(mydata);
switch_safe_free(hash_key);
}
#define HASH_API_USAGE "insert|select|delete/realm/key[/value]"
SWITCH_STANDARD_API(hash_api_function)
{
int argc = 0;
char *argv[4] = { 0 };
char *mydata = NULL;
char *value = NULL;
char *hash_key = NULL;
if (!zstr(cmd)) {
mydata = strdup(cmd);
switch_assert(mydata);
argc = switch_separate_string(mydata, '/', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc < 3 || !argv[0]) {
goto usage;
}
hash_key = switch_mprintf("%s_%s", argv[1], argv[2]);
if (!strcasecmp(argv[0], "insert")) {
if (argc < 4) {
goto usage;
}
switch_thread_rwlock_wrlock(globals.db_hash_rwlock);
if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
switch_safe_free(value);
switch_core_hash_delete(globals.db_hash, hash_key);
}
value = strdup(argv[3]);
switch_assert(value);
switch_core_hash_insert(globals.db_hash, hash_key, value);
stream->write_function(stream, "+OK\n");
} else if (!strcasecmp(argv[0], "delete")) {
switch_thread_rwlock_wrlock(globals.db_hash_rwlock);
if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
switch_safe_free(value);
switch_core_hash_delete(globals.db_hash, hash_key);
stream->write_function(stream, "+OK\n");
} else {
stream->write_function(stream, "-ERR Not found\n");
}
} else if (!strcasecmp(argv[0], "select")) {
switch_thread_rwlock_rdlock(globals.db_hash_rwlock);
if ((value = switch_core_hash_find(globals.db_hash, hash_key))) {
stream->write_function(stream, "%s", value);
}
} else {
goto usage;
}
goto done;
usage:
stream->write_function(stream, "-ERR Usage: hash %s\n", HASH_API_USAGE);
done:
switch_thread_rwlock_unlock(globals.db_hash_rwlock);
switch_safe_free(mydata);
switch_safe_free(hash_key);
return SWITCH_STATUS_SUCCESS;
}
/* INIT/DEINIT STUFF */
SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load)
{
switch_application_interface_t *app_interface;
switch_api_interface_t *commands_api_interface;
switch_limit_interface_t *limit_interface;
switch_status_t status;
memset(&globals, 0, sizeof(&globals));
globals.pool = pool;
status = switch_event_reserve_subclass(LIMIT_EVENT_USAGE);
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_INUSE) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register event subclass \"%s\" (%d)\n", LIMIT_EVENT_USAGE, status);
return SWITCH_STATUS_FALSE;
}
switch_thread_rwlock_create(&globals.limit_hash_rwlock, globals.pool);
switch_thread_rwlock_create(&globals.db_hash_rwlock, globals.pool);
switch_core_hash_init(&globals.limit_hash, pool);
switch_core_hash_init(&globals.db_hash, pool);
/* connect my internal structure to the blank pointer passed to me */
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
/* register limit interfaces */
SWITCH_ADD_LIMIT(limit_interface, "hash", limit_incr_hash, limit_release_hash, limit_usage_hash, limit_reset_hash, limit_status_hash);
switch_scheduler_add_task(switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL, limit_hash_cleanup_callback, "limit_hash_cleanup", "mod_hash", 0, NULL,
SSHF_NONE);
SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA)
SWITCH_ADD_API(commands_api_interface, "hash", "hash get/set", hash_api_function, "[insert|delete|select]/<realm>/<key>/<value>");
switch_console_set_complete("add hash insert");
switch_console_set_complete("add hash delete");
switch_console_set_complete("add hash select");
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_hash_shutdown)
{
switch_scheduler_del_task_group("mod_hash");
switch_thread_rwlock_destroy(globals.db_hash_rwlock);
switch_thread_rwlock_destroy(globals.limit_hash_rwlock);
switch_core_hash_destroy(&globals.limit_hash);
switch_core_hash_destroy(&globals.db_hash);
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:
*/

35
src/mod/applications/mod_lcr/mod_lcr.c Executable file → Normal file
View File

@ -126,7 +126,6 @@ struct profile_obj {
switch_bool_t reorder_by_rate;
switch_bool_t quote_in_list;
switch_bool_t info_in_headers;
switch_bool_t enable_sip_redir;
};
typedef struct profile_obj profile_t;
@ -295,18 +294,9 @@ static char *get_bridge_data(switch_memory_pool_t *pool, char *dialed_number, ch
user_rate = switch_core_sprintf(pool, ",lcr_user_rate=%s", cur_route->user_rate_str);
}
if (profile->enable_sip_redir) {
data =
switch_core_sprintf(pool, "%s%s%s%s%s", cur_route->gw_prefix, cur_route->prefix, destination_number, cur_route->suffix, cur_route->gw_suffix);
} else {
data =
switch_core_sprintf(pool, "[lcr_carrier=%s,lcr_rate=%s%s%s%s%s]%s%s%s%s%s", cur_route->carrier_name, cur_route->rate_str, user_rate, codec, cid,
header, cur_route->gw_prefix, cur_route->prefix, destination_number, cur_route->suffix, cur_route->gw_suffix);
}
if (session && (switch_string_var_check_const(data) || switch_string_has_escaped_data(data))) {
data = switch_channel_expand_variables(switch_core_session_get_channel(session), data);
}
data =
switch_core_sprintf(pool, "[lcr_carrier=%s,lcr_rate=%s%s%s%s%s]%s%s%s%s%s", cur_route->carrier_name, cur_route->rate_str, user_rate, codec, cid,
header, cur_route->gw_prefix, cur_route->prefix, destination_number, cur_route->suffix, cur_route->gw_suffix);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Returning Dialstring %s\n", data);
return data;
@ -415,7 +405,7 @@ static switch_bool_t db_check(char *sql)
switch_cache_db_handle_t *dbh = NULL;
if (globals.odbc_dsn && (dbh = lcr_get_db_handle())) {
if (switch_cache_db_execute_sql(dbh, sql, NULL) == SWITCH_STATUS_SUCCESS) {
if (switch_cache_db_execute_sql(dbh, sql, NULL) == SWITCH_ODBC_SUCCESS) {
ret = SWITCH_TRUE;
}
}
@ -545,7 +535,8 @@ static switch_bool_t lcr_execute_sql_callback(char *sql, switch_core_db_callback
switch_cache_db_handle_t *dbh = NULL;
if (globals.odbc_dsn && (dbh = lcr_get_db_handle())) {
if (switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, NULL) != SWITCH_STATUS_SUCCESS) {
if (switch_cache_db_execute_sql_callback(dbh, sql, callback, pdata, NULL)
== SWITCH_ODBC_FAIL) {
retval = SWITCH_FALSE;
} else {
retval = SWITCH_TRUE;
@ -912,7 +903,6 @@ static switch_status_t lcr_load_config()
char *reorder_by_rate = NULL;
char *quote_in_list = NULL;
char *info_in_headers = NULL;
char *enable_sip_redir = NULL;
char *id_s = NULL;
char *custom_sql = NULL;
int argc, x = 0;
@ -965,8 +955,6 @@ static switch_status_t lcr_load_config()
info_in_headers = val;
} else if (!strcasecmp(var, "quote_in_list") && !zstr(val)) {
quote_in_list = val;
} else if (!strcasecmp(var, "enable_sip_redir") && !zstr(val)) {
enable_sip_redir = val;
}
}
@ -1057,10 +1045,6 @@ static switch_status_t lcr_load_config()
profile->info_in_headers = switch_true(info_in_headers);
}
if (!zstr(enable_sip_redir)) {
profile->enable_sip_redir = switch_true(enable_sip_redir);
}
if (!zstr(quote_in_list)) {
profile->quote_in_list = switch_true(quote_in_list);
}
@ -1268,11 +1252,7 @@ SWITCH_STANDARD_APP(lcr_app_function)
switch_channel_set_variable(channel, vbuf, cur_route->codec);
cnt++;
if (cur_route->next) {
if (routes.profile->enable_sip_redir) {
dig_stream.write_function(&dig_stream, "%s,", cur_route->dialstring);
} else {
dig_stream.write_function(&dig_stream, "%s|", cur_route->dialstring);
}
dig_stream.write_function(&dig_stream, "%s|", cur_route->dialstring);
} else {
dig_stream.write_function(&dig_stream, "%s", cur_route->dialstring);
}
@ -1519,7 +1499,6 @@ SWITCH_STANDARD_API(dialplan_lcr_admin_function)
stream->write_function(stream, " has npanxx:\t%s\n", profile->profile_has_npanxx ? "true" : "false");
stream->write_function(stream, " Reorder rate:\t%s\n", profile->reorder_by_rate ? "enabled" : "disabled");
stream->write_function(stream, " Info in headers:\t%s\n", profile->info_in_headers ? "enabled" : "disabled");
stream->write_function(stream, " Sip Redirection Mode:\t%s\n", profile->enable_sip_redir ? "enabled" : "disabled");
stream->write_function(stream, " Quote IN() List:\t%s\n", profile->quote_in_list ? "enabled" : "disabled");
stream->write_function(stream, "\n");
}

File diff suppressed because it is too large Load Diff

View File

@ -1,153 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="mod_limit"
ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
RootNamespace="mod_limit"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
ConfigurationType="2"
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath=".\mod_limit.c"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -1392,6 +1392,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_enterprise_originate(switch_core_sess
}
}
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "ent_originate_aleg_uuid", switch_core_session_get_uuid(session));
/* A comma (,) separated list of variable names that should ne propagated from originator to originatee */
if (channel && (export_vars = switch_channel_get_variable(channel, SWITCH_EXPORT_VARS_VARIABLE))) {
char *cptmp = switch_core_session_strdup(session, export_vars);

222
src/switch_limit.c Executable file
View File

@ -0,0 +1,222 @@
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2009, 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):
*
* Rupa Schomaker <rupa@rupa.com>
*
* switch_limit.c Limit support
*
*/
#include <switch.h>
static switch_limit_interface_t *get_backend(const char *backend) {
switch_limit_interface_t *limit = NULL;
if (!backend) {
return NULL;
}
if (!(limit = switch_loadable_module_get_limit_interface(backend))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unable to locate limit backend: %s\n", backend);
}
return limit;
}
static void release_backend(switch_limit_interface_t *limit) {
if (limit) {
UNPROTECT_INTERFACE(limit);
}
}
SWITCH_DECLARE(void) switch_limit_init(switch_memory_pool_t *pool) {
if (switch_event_reserve_subclass(LIMIT_EVENT_USAGE) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldnt register event subclass \"%s\"", LIMIT_EVENT_USAGE);
}
}
SWITCH_DECLARE(void) switch_limit_fire_event(const char *backend, const char *realm, const char *key, uint32_t usage, uint32_t rate, uint32_t max, uint32_t ratemax)
{
switch_event_t *event;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, LIMIT_EVENT_USAGE) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "backend", backend);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "realm", realm);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "key", key);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "usage", "%d", usage);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rate", "%d", rate);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "max", "%d", max);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "ratemax", "%d", ratemax);
switch_event_fire(&event);
}
}
static switch_status_t limit_state_handler(switch_core_session_t *session)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_channel_state_t state = switch_channel_get_state(channel);
const char *vval = switch_channel_get_variable(channel, LIMIT_IGNORE_TRANSFER_VARIABLE);
const char *backendlist = switch_channel_get_variable(channel, LIMIT_BACKEND_VARIABLE);
int argc = 0;
char *argv[6] = { 0 };
char *mydata = NULL;
if (zstr(backendlist)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unset limit backendlist!\n");
return SWITCH_STATUS_SUCCESS;
}
mydata = strdup(backendlist);
if (state >= CS_HANGUP || (state == CS_ROUTING && !switch_true(vval))) {
int x;
argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0])));
for (x = 0; x < argc; x++) {
switch_limit_release(argv[x], session, NULL, NULL);
}
switch_core_event_hook_remove_state_change(session, limit_state_handler);
/* Remove limit_realm variable so we register another hook if limit is called again */
switch_channel_set_variable(channel, "limit_realm", NULL);
}
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_limit_incr(const char *backend, switch_core_session_t *session, const char *realm, const char *resource, const int max, const int interval) {
switch_limit_interface_t *limit = NULL;
switch_channel_t *channel = NULL;
int status = SWITCH_STATUS_SUCCESS;
assert(session);
channel = switch_core_session_get_channel(session);
/* locate impl, call appropriate func */
if (!(limit = get_backend(backend))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
switch_goto_status(SWITCH_STATUS_GENERR, end);
}
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "incr called: %s_%s max:%d, interval:%d\n",
realm, resource, max, interval);
if ((status = limit->incr(session, realm, resource, max, interval)) == SWITCH_STATUS_SUCCESS) {
/* race condition? what if another leg is doing the same thing? */
const char *existing = switch_channel_get_variable(channel, LIMIT_BACKEND_VARIABLE);
if (existing) {
if (!strstr(existing, backend)) {
switch_channel_set_variable_printf(channel, LIMIT_BACKEND_VARIABLE, "%s,%s", existing, backend);
}
} else {
switch_channel_set_variable(channel, LIMIT_BACKEND_VARIABLE, backend);
switch_core_event_hook_add_state_change(session, limit_state_handler);
}
}
release_backend(limit);
end:
return status;
}
SWITCH_DECLARE(switch_status_t) switch_limit_release(const char *backend, switch_core_session_t *session, const char *realm, const char *resource) {
switch_limit_interface_t *limit = NULL;
int status = SWITCH_STATUS_SUCCESS;
/* locate impl, call appropriate func */
if (!(limit = get_backend(backend))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
switch_goto_status(SWITCH_STATUS_GENERR, end);
}
status = limit->release(session, realm, resource);
end:
release_backend(limit);
return status;
}
SWITCH_DECLARE(int) switch_limit_usage(const char *backend, const char *realm, const char *resource, uint32_t *rcount) {
switch_limit_interface_t *limit = NULL;
int usage = 0;
/* locate impl, call appropriate func */
if (!(limit = get_backend(backend))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
goto end;
}
usage = limit->usage(realm, resource, rcount);
end:
release_backend(limit);
return usage;
}
SWITCH_DECLARE(switch_status_t) switch_limit_reset(const char *backend) {
switch_limit_interface_t *limit = NULL;
int status = SWITCH_STATUS_SUCCESS;
/* locate impl, call appropriate func */
if (!(limit = get_backend(backend))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
switch_goto_status(SWITCH_STATUS_GENERR, end);
}
status = limit->reset();
end:
release_backend(limit);
return status;
}
SWITCH_DECLARE(char *) switch_limit_status(const char *backend) {
switch_limit_interface_t *limit = NULL;
char *status = NULL;
/* locate impl, call appropriate func */
if (!(limit = get_backend(backend))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Limit subsystem %s not found!\n", backend);
switch_goto_status("-ERR", end);
}
status = limit->status();
end:
release_backend(limit);
return status;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -71,6 +71,7 @@ struct switch_loadable_module_container {
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;
};
@ -433,6 +434,32 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable
}
}
}
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;
@ -768,6 +795,23 @@ static switch_status_t switch_loadable_module_unprocess(switch_loadable_module_t
}
}
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;
@ -1164,6 +1208,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init()
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);
@ -1354,6 +1399,7 @@ SWITCH_DECLARE(void) switch_loadable_module_shutdown(void)
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);
@ -1411,17 +1457,18 @@ SWITCH_DECLARE(switch_codec_interface_t *) switch_loadable_module_get_codec_inte
}
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(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)
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);
}
@ -1691,6 +1738,9 @@ SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_m
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;