Diff
Modified: freeswitch/branches/rupa_limit/Makefile.am (16914 => 16915)
--- freeswitch/branches/rupa_limit/Makefile.am 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/Makefile.am 2010-03-05 16:51:03 UTC (rev 16915)
@@ -171,6 +171,7 @@
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 = \
@@ -225,6 +226,7 @@
src/switch_config.c \
src/switch_time.c \
src/switch_odbc.c \
+ src/switch_limit.c \
src/g711.c \
src/switch_pcm.c \
libs/stfu/stfu.c \
Modified: freeswitch/branches/rupa_limit/build/modules.conf.in (16914 => 16915)
--- freeswitch/branches/rupa_limit/build/modules.conf.in 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/build/modules.conf.in 2010-03-05 16:51:03 UTC (rev 16915)
@@ -9,10 +9,11 @@
applications/mod_fifo
#applications/mod_fax
#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
Modified: freeswitch/branches/rupa_limit/src/include/switch.h (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/include/switch.h 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch.h 2010-03-05 16:51:03 UTC (rev 16915)
@@ -131,6 +131,7 @@
#include "switch_config.h"
#include "switch_nat.h"
#include "switch_odbc.h"
+#include "switch_limit.h"
#include <libteletone.h>
Modified: freeswitch/branches/rupa_limit/src/include/switch_loadable_module.h (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/include/switch_loadable_module.h 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch_loadable_module.h 2010-03-05 16:51:03 UTC (rev 16915)
@@ -81,6 +81,8 @@
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;
@@ -205,6 +207,13 @@
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
\param arraylen the max size in elements of the array
@@ -315,6 +324,18 @@
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) \
Modified: freeswitch/branches/rupa_limit/src/include/switch_module_interfaces.h (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/include/switch_module_interfaces.h 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch_module_interfaces.h 2010-03-05 16:51:03 UTC (rev 16915)
@@ -512,6 +512,28 @@
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);
+ /*! 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 */
Modified: freeswitch/branches/rupa_limit/src/include/switch_types.h (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/include/switch_types.h 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/include/switch_types.h 2010-03-05 16:51:03 UTC (rev 16915)
@@ -264,7 +264,8 @@
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 {
@@ -1503,6 +1504,7 @@
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;
Modified: freeswitch/branches/rupa_limit/src/mod/applications/mod_commands/mod_commands.c (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_commands/mod_commands.c 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_commands/mod_commands.c 2010-03-05 16:51:03 UTC (rev 16915)
@@ -3201,7 +3201,7 @@
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];
@@ -3259,6 +3259,7 @@
!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';
@@ -4048,6 +4049,95 @@
return SWITCH_STATUS_SUCCESS;
}
+/* LIMIT Stuff */
+#define LIMIT_USAGE_USAGE "<backend> <realm> <id>"
+SWITCH_STANDARD_API(limit_usage_function)
+{
+ int argc = 0;
+ char *argv[5] = { 0 };
+ char *mydata = NULL;
+ uint32_t count = 0;
+
+ 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;
+ }
+
+ count = switch_limit_usage(argv[0], argv[1], argv[2]);
+
+ 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;
@@ -4108,6 +4198,9 @@
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, "md5", "md5", md5_function, "<data>");
SWITCH_ADD_API(commands_api_interface, "module_exists", "check if module exists", module_exists_function, "<module>");
Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_db/Makefile (0 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_db/Makefile (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_db/Makefile 2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,2 @@
+BASE=../../../..
+include $(BASE)/build/modmake.rules
Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.2008.vcproj (0 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.2008.vcproj (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.2008.vcproj 2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="mod_db"
+ ProjectGUID="{F6A33240-8F29-48BD-98F0-826995911799}"
+ RootNamespace="mod_db"
+ 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_db.c"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.c (0 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.c (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_db/mod_db.c 2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,657 @@
+/*
+ * 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);
+
+ /* 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:
+ */
Modified: freeswitch/branches/rupa_limit/src/mod/applications/mod_dptools/mod_dptools.c (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_dptools/mod_dptools.c 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_dptools/mod_dptools.c 2010-03-05 16:51:03 UTC (rev 16915)
@@ -2977,6 +2977,140 @@
}
}
+/* 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[2], '/'))) {
+ *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"
@@ -3144,6 +3278,8 @@
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);
Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/Makefile (0 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/Makefile (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/Makefile 2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,2 @@
+BASE=../../../..
+include $(BASE)/build/modmake.rules
Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.2008.vcproj (0 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.2008.vcproj (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.2008.vcproj 2010-03-05 16:51:03 UTC (rev 16915)
@@ -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>
Added: freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.c (0 => 16915)
--- freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.c (rev 0)
+++ freeswitch/branches/rupa_limit/src/mod/applications/mod_hash/mod_hash.c 2010-03-05 16:51:03 UTC (rev 16915)
@@ -0,0 +1,457 @@
+/*
+ * 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>
+
+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_mutex_t *mutex;
+ switch_mutex_t *limit_hash_mutex;
+ switch_hash_t *limit_hash;
+ 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;
+
+/* 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_mutex_lock(globals.limit_hash_mutex);
+ /* 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 */
+ 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 and add a state handler */
+ 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) {
+ if (item->last_check <= (now - interval)) {
+ item->rate_usage = 1;
+ item->last_check = now;
+ } 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_mutex_unlock(globals.limit_hash_mutex);
+ return status;
+}
+
+/* !\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_mutex_lock(globals.limit_hash_mutex);
+
+ /* 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) {
+ /* 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) {
+ /* Noone is using this item anymore */
+ switch_core_hash_delete(globals.limit_hash, (const char *) hashkey);
+ free(item);
+ }
+ }
+ }
+
+ switch_mutex_unlock(globals.limit_hash_mutex);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_LIMIT_USAGE(limit_usage_hash)
+{
+ char *hash_key = NULL;
+ limit_hash_item_t *item = NULL;
+ int count = 0;
+
+ switch_mutex_lock(globals.limit_hash_mutex);
+
+ hash_key = switch_mprintf("%s_%s", realm, resource);
+
+ if ((item = switch_core_hash_find(globals.limit_hash, hash_key))) {
+ count = item->total_usage;
+ }
+
+ switch_safe_free(hash_key);
+ switch_mutex_unlock(globals.limit_hash_mutex);
+
+ 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_mutex_lock(globals.limit_hash_mutex);
+
+ while ((hi = switch_hash_first(NULL, globals.limit_hash))) {
+ count++;
+ }
+ switch_mutex_unlock(globals.limit_hash_mutex);
+
+ ret = switch_mprintf("There are %d elements being tracked.", count);
+ return ret;
+}
+
+/* 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_mutex_lock(globals.db_hash_mutex);
+
+ 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_mutex_unlock(globals.db_hash_mutex);
+ 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;
+
+ switch_mutex_lock(globals.db_hash_mutex);
+
+ 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;
+ }
+ 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")) {
+ 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")) {
+ 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_mutex_unlock(globals.db_hash_mutex);
+ 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;
+
+ memset(&globals, 0, sizeof(&globals));
+ globals.pool = 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);
+ return SWITCH_STATUS_FALSE;
+ }
+
+ switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_mutex_init(&globals.limit_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
+ switch_mutex_init(&globals.db_hash_mutex, SWITCH_MUTEX_NESTED, 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_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_mutex_destroy(globals.mutex);
+ switch_mutex_destroy(globals.limit_hash_mutex);
+
+ switch_core_hash_destroy(&globals.limit_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:
+ */
Modified: freeswitch/branches/rupa_limit/src/switch_core.c (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/switch_core.c 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/switch_core.c 2010-03-05 16:51:03 UTC (rev 16915)
@@ -1290,7 +1290,6 @@
runtime.timer_affinity = -1;
switch_load_core_config("switch.conf");
-
switch_core_state_machine_init(runtime.memory_pool);
if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
@@ -1301,6 +1300,8 @@
switch_rtp_init(runtime.memory_pool);
+ switch_nat_init(runtime.memory_pool);
+
runtime.running = 1;
runtime.initiated = switch_time_now();
Modified: freeswitch/branches/rupa_limit/src/switch_loadable_module.c (16914 => 16915)
--- freeswitch/branches/rupa_limit/src/switch_loadable_module.c 2010-03-05 14:54:56 UTC (rev 16914)
+++ freeswitch/branches/rupa_limit/src/switch_loadable_module.c 2010-03-05 16:51:03 UTC (rev 16915)
@@ -71,6 +71,7 @@
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,7 +434,33 @@
}
}
}
+ 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 @@
}
}
+ 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_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);
@@ -1347,6 +1392,7 @@
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);
}
@@ -1403,17 +1449,18 @@
}
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);
}
@@ -1683,6 +1730,9 @@
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;