Add redis backend to limit
This commit is contained in:
parent
8c8d962e3a
commit
8ac3bdca6e
|
@ -0,0 +1,7 @@
|
|||
<configuration name="redis.conf" description="mod_redis Configuration">
|
||||
<settings>
|
||||
<param name="host" value="localhost"/>
|
||||
<param name="port" value="6379"/>
|
||||
<param name="timeout" value="10000"/>
|
||||
</settings>
|
||||
</configuration>
|
|
@ -0,0 +1,3 @@
|
|||
BASE=../../../..
|
||||
LOCAL_OBJS=credis.o
|
||||
include $(BASE)/build/modmake.rules
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,327 @@
|
|||
/* credis.h -- a C client library for Redis, public API.
|
||||
*
|
||||
* Copyright (c) 2009-2010, Jonas Romfelt <jonas at romfelt dot se>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __CREDIS_H
|
||||
#define __CREDIS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Functions below should map quite nicely to Redis 1.02 command set.
|
||||
* Refer to the official Redis documentation for further explanation of
|
||||
* each command. See credis examples that show how functions can be used.
|
||||
* Here is a brief example that connects to a Redis server and sets value
|
||||
* of key `fruit' to `banana':
|
||||
*
|
||||
* REDIS rh = credis_connect("localhost", 6789, 2000);
|
||||
* credis_set(rh, "fruit", "banana");
|
||||
* credis_close(rh);
|
||||
*
|
||||
* In general, functions return 0 on success or a negative value on
|
||||
* error. Refer to CREDIS_ERR_* codes. The return code -1 is typically
|
||||
* used when for instance a key is not found.
|
||||
*
|
||||
* IMPORTANT! Memory buffers are allocated, used and managed by credis
|
||||
* internally. Subsequent calls to credis functions _will_ destroy the
|
||||
* data to which returned values reference to. If for instance the
|
||||
* returned value by a call to credis_get() is to be used later in the
|
||||
* program, a strdup() is highly recommended. However, each `REDIS'
|
||||
* handle has its own state and manages its own memory buffer
|
||||
* independently. That means that one of two handles can be destroyed
|
||||
* while the other keeps its connection and data.
|
||||
*
|
||||
* TODO
|
||||
* - Currently only support for zero-terminated strings, not for storing
|
||||
* abritary binary data as bulk data. Basically an API issue since it
|
||||
* is partially supported internally.
|
||||
* - Support for Redis >= 1.1 protocol
|
||||
*/
|
||||
|
||||
/* handle to a Redis server connection */
|
||||
typedef struct _cr_redis* REDIS;
|
||||
|
||||
#define CREDIS_OK 0
|
||||
#define CREDIS_ERR -90
|
||||
#define CREDIS_ERR_NOMEM -91
|
||||
#define CREDIS_ERR_RESOLVE -92
|
||||
#define CREDIS_ERR_CONNECT -93
|
||||
#define CREDIS_ERR_SEND -94
|
||||
#define CREDIS_ERR_RECV -95
|
||||
#define CREDIS_ERR_TIMEOUT -96
|
||||
#define CREDIS_ERR_PROTOCOL -97
|
||||
|
||||
#define CREDIS_TYPE_NONE 1
|
||||
#define CREDIS_TYPE_STRING 2
|
||||
#define CREDIS_TYPE_LIST 3
|
||||
#define CREDIS_TYPE_SET 4
|
||||
|
||||
#define CREDIS_SERVER_MASTER 1
|
||||
#define CREDIS_SERVER_SLAVE 2
|
||||
|
||||
#define CREDIS_VERSION_STRING_SIZE 32
|
||||
|
||||
typedef struct _cr_info {
|
||||
char redis_version[CREDIS_VERSION_STRING_SIZE];
|
||||
int bgsave_in_progress;
|
||||
int connected_clients;
|
||||
int connected_slaves;
|
||||
unsigned int used_memory;
|
||||
long long changes_since_last_save;
|
||||
int last_save_time;
|
||||
long long total_connections_received;
|
||||
long long total_commands_processed;
|
||||
int uptime_in_seconds;
|
||||
int uptime_in_days;
|
||||
int role;
|
||||
} REDIS_INFO;
|
||||
|
||||
|
||||
/*
|
||||
* Connection handling
|
||||
*/
|
||||
|
||||
/* setting host to NULL will use "localhost". setting port to 0 will use
|
||||
* default port 6379 */
|
||||
REDIS credis_connect(const char *host, int port, int timeout);
|
||||
|
||||
void credis_close(REDIS rhnd);
|
||||
|
||||
void credis_quit(REDIS rhnd);
|
||||
|
||||
int credis_auth(REDIS rhnd, const char *password);
|
||||
|
||||
int credis_ping(REDIS rhnd);
|
||||
|
||||
/*
|
||||
* Commands operating on string values
|
||||
*/
|
||||
|
||||
int credis_set(REDIS rhnd, const char *key, const char *val);
|
||||
|
||||
/* returns -1 if the key doesn't exists */
|
||||
int credis_get(REDIS rhnd, const char *key, char **val);
|
||||
|
||||
/* returns -1 if the key doesn't exists */
|
||||
int credis_getset(REDIS rhnd, const char *key, const char *set_val, char **get_val);
|
||||
|
||||
/* returns number of values returned in vector `valv'. `keyc' is the number of
|
||||
* keys stored in `keyv'. */
|
||||
int credis_mget(REDIS rhnd, int keyc, const char **keyv, char ***valv);
|
||||
|
||||
/* returns -1 if the key already exists and hence not set */
|
||||
int credis_setnx(REDIS rhnd, const char *key, const char *val);
|
||||
|
||||
int credis_incr(REDIS rhnd, const char *key, int *new_val);
|
||||
|
||||
int credis_incrby(REDIS rhnd, const char *key, int incr_val, int *new_val);
|
||||
|
||||
int credis_decr(REDIS rhnd, const char *key, int *new_val);
|
||||
|
||||
int credis_decrby(REDIS rhnd, const char *key, int decr_val, int *new_val);
|
||||
|
||||
/* returns -1 if the key doesn't exists and 0 if it does */
|
||||
int credis_exists(REDIS rhnd, const char *key);
|
||||
|
||||
/* returns -1 if the key doesn't exists and 0 if it was removed */
|
||||
int credis_del(REDIS rhnd, const char *key);
|
||||
|
||||
/* returns type, refer to CREDIS_TYPE_* defines */
|
||||
int credis_type(REDIS rhnd, const char *key);
|
||||
|
||||
/* TODO for Redis >= 1.1
|
||||
* MSET key1 value1 key2 value2 ... keyN valueN set a multiple keys to multiple values in a single atomic operation
|
||||
* MSETNX key1 value1 key2 value2 ... keyN valueN set a multiple keys to multiple values in a single atomic operation if none of
|
||||
* DEL key1 key2 ... keyN remove multiple keys
|
||||
*/
|
||||
|
||||
/*
|
||||
* Commands operating on key space
|
||||
*/
|
||||
|
||||
int credis_keys(REDIS rhnd, const char *pattern, char **keyv, int len);
|
||||
|
||||
int credis_randomkey(REDIS rhnd, char **key);
|
||||
|
||||
int credis_rename(REDIS rhnd, const char *key, const char *new_key_name);
|
||||
|
||||
/* returns -1 if the key already exists */
|
||||
int credis_renamenx(REDIS rhnd, const char *key, const char *new_key_name);
|
||||
|
||||
/* returns size of db */
|
||||
int credis_dbsize(REDIS rhnd);
|
||||
|
||||
/* returns -1 if the timeout was not set; either due to key already has
|
||||
an associated timeout or key does not exist */
|
||||
int credis_expire(REDIS rhnd, const char *key, int secs);
|
||||
|
||||
/* returns time to live seconds or -1 if key does not exists or does not
|
||||
* have expire set */
|
||||
int credis_ttl(REDIS rhnd, const char *key);
|
||||
|
||||
/*
|
||||
* Commands operating on lists
|
||||
*/
|
||||
|
||||
int credis_rpush(REDIS rhnd, const char *key, const char *element);
|
||||
|
||||
int credis_lpush(REDIS rhnd, const char *key, const char *element);
|
||||
|
||||
/* returns length of list */
|
||||
int credis_llen(REDIS rhnd, const char *key);
|
||||
|
||||
/* returns number of elements returned in vector `elementv' */
|
||||
int credis_lrange(REDIS rhnd, const char *key, int start, int range, char ***elementv);
|
||||
|
||||
int credis_ltrim(REDIS rhnd, const char *key, int start, int end);
|
||||
|
||||
/* returns -1 if the key doesn't exists */
|
||||
int credis_lindex(REDIS rhnd, const char *key, int index, char **element);
|
||||
|
||||
int credis_lset(REDIS rhnd, const char *key, int index, const char *element);
|
||||
|
||||
/* returns number of elements removed */
|
||||
int credis_lrem(REDIS rhnd, const char *key, int count, const char *element);
|
||||
|
||||
/* returns -1 if the key doesn't exists */
|
||||
int credis_lpop(REDIS rhnd, const char *key, char **val);
|
||||
|
||||
/* returns -1 if the key doesn't exists */
|
||||
int credis_rpop(REDIS rhnd, const char *key, char **val);
|
||||
|
||||
/* TODO for Redis >= 1.1
|
||||
* RPOPLPUSH srckey dstkey
|
||||
*
|
||||
* TODO for Redis >= 1.3.1
|
||||
* BLPOP key1 key2 ... keyN timeout
|
||||
* BRPOP key1 key2 ... keyN timeout
|
||||
*/
|
||||
|
||||
/*
|
||||
* Commands operating on sets
|
||||
*/
|
||||
|
||||
/* returns -1 if the given member was already a member of the set */
|
||||
int credis_sadd(REDIS rhnd, const char *key, const char *member);
|
||||
|
||||
/* returns -1 if the given member is not a member of the set */
|
||||
int credis_srem(REDIS rhnd, const char *key, const char *member);
|
||||
|
||||
/* returns -1 if the key doesn't exists and 0 if it does */
|
||||
int credis_sismember(REDIS rhnd, const char *key, const char *member);
|
||||
|
||||
/* returns -1 if the given key doesn't exists else value is returned in `member' */
|
||||
int credis_spop(REDIS rhnd, const char *key, char **member);
|
||||
|
||||
/* returns -1 if the member doesn't exists in the source set */
|
||||
int credis_smove(REDIS rhnd, const char *sourcekey, const char *destkey,
|
||||
const char *member);
|
||||
|
||||
/* returns cardinality (number of members) or 0 if the given key doesn't exists */
|
||||
int credis_scard(REDIS rhnd, const char *key);
|
||||
|
||||
/* returns number of members returned in vector `members'. `keyc' is the number of
|
||||
* keys stored in `keyv'. */
|
||||
int credis_sinter(REDIS rhnd, int keyc, const char **keyv, char ***members);
|
||||
|
||||
/* `keyc' is the number of keys stored in `keyv' */
|
||||
int credis_sinterstore(REDIS rhnd, const char *destkey, int keyc, const char **keyv);
|
||||
|
||||
/* returns number of members returned in vector `members'. `keyc' is the number of
|
||||
* keys stored in `keyv'. */
|
||||
int credis_sunion(REDIS rhnd, int keyc, const char **keyv, char ***members);
|
||||
|
||||
/* `keyc' is the number of keys stored in `keyv' */
|
||||
int credis_sunionstore(REDIS rhnd, const char *destkey, int keyc, const char **keyv);
|
||||
|
||||
/* returns number of members returned in vector `members'. `keyc' is the number of
|
||||
* keys stored in `keyv'. */
|
||||
int credis_sdiff(REDIS rhnd, int keyc, const char **keyv, char ***members);
|
||||
|
||||
/* `keyc' is the number of keys stored in `keyv' */
|
||||
int credis_sdiffstore(REDIS rhnd, const char *destkey, int keyc, const char **keyv);
|
||||
|
||||
/* returns number of members returned in vector `members' */
|
||||
int credis_smembers(REDIS rhnd, const char *key, char ***members);
|
||||
|
||||
/* TODO Redis >= 1.1
|
||||
* SRANDMEMBER key Return a random member of the Set value at key
|
||||
*/
|
||||
|
||||
/*
|
||||
* Multiple databases handling commands
|
||||
*/
|
||||
|
||||
int credis_select(REDIS rhnd, int index);
|
||||
|
||||
/* returns -1 if the key was not moved; already present at target
|
||||
* or not found on current db */
|
||||
int credis_move(REDIS rhnd, const char *key, int index);
|
||||
|
||||
int credis_flushdb(REDIS rhnd);
|
||||
|
||||
int credis_flushall(REDIS rhnd);
|
||||
|
||||
/*
|
||||
* Sorting
|
||||
*/
|
||||
|
||||
/* returns number of elements returned in vector `elementv' */
|
||||
int credis_sort(REDIS rhnd, const char *query, char ***elementv);
|
||||
|
||||
/*
|
||||
* Persistence control commands
|
||||
*/
|
||||
|
||||
int credis_save(REDIS rhnd);
|
||||
|
||||
int credis_bgsave(REDIS rhnd);
|
||||
|
||||
/* returns UNIX time stamp of last successfull save to disk */
|
||||
int credis_lastsave(REDIS rhnd);
|
||||
|
||||
int credis_shutdown(REDIS rhnd);
|
||||
|
||||
/*
|
||||
* Remote server control commands
|
||||
*/
|
||||
|
||||
int credis_info(REDIS rhnd, REDIS_INFO *info);
|
||||
|
||||
int credis_monitor(REDIS rhnd);
|
||||
|
||||
/* setting host to NULL and/or port to 0 will turn off replication */
|
||||
int credis_slaveof(REDIS rhnd, const char *host, int port);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CREDIS_H */
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* 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):
|
||||
* Kevin Morizur <kmorizur@avgs.ca>
|
||||
* Mathieu Rene <mrene@avgs.ca>
|
||||
*
|
||||
* mod_redis.c -- Redis limit backend
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include "credis.h"
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_redis_load);
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_redis_shutdown);
|
||||
SWITCH_MODULE_DEFINITION(mod_redis, mod_redis_load, NULL, mod_redis_shutdown);
|
||||
|
||||
static struct{
|
||||
char *host;
|
||||
int port;
|
||||
int timeout;
|
||||
} globals;
|
||||
|
||||
static switch_xml_config_item_t instructions[] = {
|
||||
/* parameter name type reloadable pointer default value options structure */
|
||||
SWITCH_CONFIG_ITEM_STRING_STRDUP("host", CONFIG_RELOAD, &globals.host, NULL, "localhost", "Hostname for redis server"),
|
||||
SWITCH_CONFIG_ITEM("port", SWITCH_CONFIG_INT, CONFIG_RELOADABLE, &globals.port, (void *) 6379, NULL,NULL, NULL),
|
||||
SWITCH_CONFIG_ITEM("timeout", SWITCH_CONFIG_INT, CONFIG_RELOADABLE, &globals.timeout, (void *) 10000, NULL,NULL, NULL),
|
||||
SWITCH_CONFIG_ITEM_END()
|
||||
};
|
||||
|
||||
/* HASH STUFF */
|
||||
typedef struct {
|
||||
switch_hash_t *hash;
|
||||
switch_mutex_t *mutex;
|
||||
} limit_redis_private_t;
|
||||
|
||||
static switch_status_t redis_factory(REDIS *redis)
|
||||
{
|
||||
if (!((*redis) = credis_connect(globals.host, globals.port, globals.timeout))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't connect to redis server at %s:%d timeout:%d\n", globals.host, globals.port, globals.timeout);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* \brief Enforces limit_redis 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_redis)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
limit_redis_private_t *pvt = NULL;
|
||||
int val,uuid_val;
|
||||
char *rediskey = NULL;
|
||||
char *uuid_rediskey = NULL;
|
||||
uint8_t increment = 1;
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
REDIS redis;
|
||||
|
||||
if (redis_factory(&redis) != SWITCH_STATUS_SUCCESS) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
/* Get the keys for redis server */
|
||||
uuid_rediskey = switch_core_session_sprintf(session,"%s_%s_%s", switch_core_get_variable("hostname"), realm, resource);
|
||||
rediskey = switch_core_session_sprintf(session, "%s_%s", realm, resource);
|
||||
|
||||
if ((pvt = switch_channel_get_private(channel, "limit_redis"))) {
|
||||
increment = !switch_core_hash_find_locked(pvt->hash, rediskey, pvt->mutex);
|
||||
} else {
|
||||
/* This is the first limit check on this channel, create a hashtable, set our prviate data and add a state handler */
|
||||
pvt = (limit_redis_private_t *) switch_core_session_alloc(session, sizeof(limit_redis_private_t));
|
||||
switch_core_hash_init(&pvt->hash, switch_core_session_get_pool(session));
|
||||
switch_mutex_init(&pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
|
||||
switch_channel_set_private(channel, "limit_redis", pvt);
|
||||
}
|
||||
|
||||
if (!(switch_core_hash_find_locked(pvt->hash, rediskey, pvt->mutex))) {
|
||||
switch_core_hash_insert_locked(pvt->hash, rediskey, rediskey, pvt->mutex);
|
||||
}
|
||||
|
||||
if (increment) {
|
||||
if (credis_incr(redis, rediskey, &val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't increment value corresponding to %s\n", rediskey);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
|
||||
if (max > 0) {
|
||||
if (val > max){
|
||||
if (credis_decr(redis, rediskey, &val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Couldn't decrement value corresponding to %s\n", rediskey);
|
||||
switch_goto_status(SWITCH_STATUS_GENERR, end);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Usage for %s exceeds maximum rate of %d\n",
|
||||
rediskey, max);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
} else {
|
||||
if (credis_incr(redis, uuid_rediskey, &uuid_val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Couldn't increment value corresponding to %s\n", uuid_rediskey);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Limit incr redis : rediskey : %s val : %d max : %d\n", rediskey, val, max);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Limit incr redis : uuid_rediskey : %s uuid_val : %d max : %d\n", uuid_rediskey,uuid_val,max);
|
||||
*/
|
||||
end:
|
||||
if (redis) {
|
||||
credis_close(redis);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* !\brief Releases usage of a limit_redis-controlled ressource */
|
||||
SWITCH_LIMIT_RELEASE(limit_release_redis)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
limit_redis_private_t *pvt = switch_channel_get_private(channel, "limit_redis");
|
||||
int val, uuid_val;
|
||||
switch_hash_index_t *hi;
|
||||
char *rediskey = NULL;
|
||||
char *uuid_rediskey = NULL;
|
||||
int status = SWITCH_STATUS_SUCCESS;
|
||||
REDIS redis;
|
||||
|
||||
if (!pvt || !pvt->hash) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "No hashtable for channel %s\n", switch_channel_get_name(channel));
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (redis_factory(&redis) != SWITCH_STATUS_SUCCESS) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
switch_mutex_lock(pvt->mutex);
|
||||
|
||||
/* clear for uuid */
|
||||
if (realm == NULL && resource == NULL) {
|
||||
/* Loop through the channel's hashtable which contains mapping to all the limit_redis_item_t referenced by that channel */
|
||||
while ((hi = switch_hash_first(NULL, pvt->hash))) {
|
||||
void *p_val = NULL;
|
||||
const void *p_key;
|
||||
char *p_uuid_key = NULL;
|
||||
switch_ssize_t keylen;
|
||||
|
||||
switch_hash_this(hi, &p_key, &keylen, &p_val);
|
||||
|
||||
if (credis_decr(redis, (const char*)p_key, &val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Couldn't decrement value corresponding to %s\n", (char *)p_key);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
p_uuid_key = switch_core_session_sprintf(session, "%s_%s", switch_core_get_variable("hostname"), (char *)p_key);
|
||||
if (credis_decr(redis,p_uuid_key,&uuid_val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Couldn't decrement value corresponding to %s\n", p_uuid_key);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
switch_core_hash_delete(pvt->hash, (const char *) p_key);
|
||||
/*
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Limit release redis : rediskey : %s val : %d\n", (char *)p_val,val);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Limit incr redis : uuid_rediskey : %s uuid_val : %d\n",
|
||||
p_uuid_key, uuid_val);*/
|
||||
}
|
||||
|
||||
} else {
|
||||
rediskey = switch_core_session_sprintf(session, "%s_%s", realm, resource);
|
||||
uuid_rediskey = switch_core_session_sprintf(session, "%s_%s_%s", switch_core_get_variable("hostname"), realm, resource);
|
||||
switch_core_hash_delete(pvt->hash, (const char *) rediskey);
|
||||
|
||||
if (credis_decr(redis, rediskey, &val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Couldn't decrement value corresponding to %s\n", rediskey);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
if (credis_decr(redis, uuid_rediskey, &uuid_val) != 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Couldn't decrement value corresponding to %s\n", uuid_rediskey);
|
||||
switch_goto_status(SWITCH_STATUS_FALSE, end);
|
||||
}
|
||||
|
||||
/*
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Limit release redis : rediskey : %s val : %d\n", rediskey,val);
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Limit incr redis : uuid_rediskey : %s uuid_val : %d\n", uuid_rediskey,uuid_val);
|
||||
*/
|
||||
}
|
||||
end:
|
||||
switch_mutex_unlock(pvt->mutex);
|
||||
if (redis) {
|
||||
credis_close(redis);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
SWITCH_LIMIT_USAGE(limit_usage_redis)
|
||||
{
|
||||
char *redis_key;
|
||||
char *str;
|
||||
REDIS redis;
|
||||
int usage;
|
||||
|
||||
if (redis_factory(&redis) != SWITCH_STATUS_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
redis_key = switch_mprintf("%s_%s", realm, resource);
|
||||
|
||||
if (credis_get(redis, redis_key, &str) != 0){
|
||||
usage = 0;
|
||||
} else {
|
||||
usage = atoi(str);
|
||||
}
|
||||
|
||||
if (redis) {
|
||||
credis_close(redis);
|
||||
}
|
||||
|
||||
switch_safe_free(redis_key);
|
||||
return usage;
|
||||
}
|
||||
|
||||
SWITCH_LIMIT_RESET(limit_reset_redis)
|
||||
{
|
||||
REDIS redis;
|
||||
if (redis_factory(&redis) == SWITCH_STATUS_SUCCESS) {
|
||||
char *rediskey = switch_mprintf("%s_*", switch_core_get_variable("hostname"));
|
||||
int dec = 0, val = 0, keyc;
|
||||
char *uuids[2000];
|
||||
|
||||
if ((keyc = credis_keys(redis, rediskey, uuids, switch_arraylen(uuids))) > 0) {
|
||||
int i = 0;
|
||||
int hostnamelen = strlen(switch_core_get_variable("hostname"))+1;
|
||||
|
||||
for (i = 0; i < keyc && uuids[i]; i++){
|
||||
const char *key = uuids[i] + hostnamelen;
|
||||
char *value;
|
||||
|
||||
if (strlen(uuids[i]) <= hostnamelen) {
|
||||
continue; /* Sanity check */
|
||||
}
|
||||
|
||||
credis_get(redis, key, &value);
|
||||
dec = atoi(value);
|
||||
credis_decrby(redis, key, dec, &val);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DECR %s by %d. value is now %d\n", key, dec, val);
|
||||
}
|
||||
}
|
||||
switch_safe_free(rediskey);
|
||||
credis_close(redis);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't check/clear old redis entries\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
SWITCH_LIMIT_STATUS(limit_status_redis)
|
||||
{
|
||||
char *ret = switch_mprintf("This function is not yet available for Redis DB");
|
||||
return ret;
|
||||
}
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_redis_load)
|
||||
{
|
||||
switch_limit_interface_t *limit_interface = NULL;
|
||||
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
if (switch_xml_config_parse_module_settings("redis.conf", SWITCH_FALSE, instructions) != SWITCH_STATUS_SUCCESS) {
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
/* If FreeSWITCH was restarted and we still have active calls, decrement them so our global count stays valid */
|
||||
limit_reset_redis();
|
||||
|
||||
SWITCH_ADD_LIMIT(limit_interface, "redis", limit_incr_redis, limit_release_redis, limit_usage_redis, limit_reset_redis, limit_status_redis);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_redis_shutdown)
|
||||
{
|
||||
|
||||
switch_xml_config_cleanup(instructions);
|
||||
|
||||
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:
|
||||
*/
|
Loading…
Reference in New Issue