mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-04-13 07:45:26 +00:00
add API for memcached
have to run through valgrind still git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@12871 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
269851a117
commit
9fa1f0f2ec
31
src/mod/applications/mod_memcache/Makefile
Normal file
31
src/mod/applications/mod_memcache/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
MEMCACHED=libmemcached-0.27
|
||||
switch_srcdir=../../../..
|
||||
|
||||
WANT_CURL=yes
|
||||
|
||||
MEMCACHED_DIR=$(switch_srcdir)/libs/$(MEMCACHED)
|
||||
|
||||
MEMCACHEDLA=$(MEMCACHED_DIR)/libmemcached/libmemcached.la
|
||||
|
||||
LOCAL_CFLAGS=-I$(MEMCACHED_DIR)
|
||||
LOCAL_LIBADD=$(MEMCACHEDLA)
|
||||
|
||||
include $(switch_srcdir)/build/modmake.rules
|
||||
|
||||
DEFAULT_ARGS=--prefix=$(PREFIX) --disable-shared --with-pic
|
||||
|
||||
$(LOCAL_OBJS): $(LOCAL_SOURCES)
|
||||
|
||||
$(MEMCACHED_DIR):
|
||||
$(GETLIB) $(MEMCACHED).tar.gz
|
||||
|
||||
$(MEMCACHED_DIR)/Makefile: $(MEMCACHED_DIR)
|
||||
cd $(MEMCACHED_DIR) && CFLAGS=$(AM_CFLAGS) CC=$(CC) CXX=$(CXX) ./configure --disable-shared --with-pic CPPFLAGS= LDFLAGS=
|
||||
$(TOUCH_TARGET)
|
||||
|
||||
$(MEMCACHEDLA): $(MEMCACHED_DIR)/Makefile
|
||||
cd $(MEMCACHED_DIR) && $(MAKE)
|
||||
$(TOUCH_TARGET)
|
||||
|
||||
|
||||
|
391
src/mod/applications/mod_memcache/mod_memcache.c
Executable file
391
src/mod/applications/mod_memcache/mod_memcache.c
Executable file
@ -0,0 +1,391 @@
|
||||
/*
|
||||
* 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>
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Neal Horman <neal at wanlink dot com>
|
||||
*
|
||||
*
|
||||
* mod_memcache.c -- API for memcache
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
#include <libmemcached/memcached.h>
|
||||
|
||||
/* Prototypes */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_memcache_shutdown);
|
||||
SWITCH_MODULE_RUNTIME_FUNCTION(mod_memcache_runtime);
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load);
|
||||
|
||||
/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
|
||||
* Defines a switch_loadable_module_function_table_t and a static const char[] modname
|
||||
*/
|
||||
SWITCH_MODULE_DEFINITION(mod_memcache, mod_memcache_load, mod_memcache_shutdown, NULL);
|
||||
|
||||
static char *SYNTAX = "memcache <set|replace|add> <key> <value> [expiration [flags]]\n"
|
||||
"memcache <getflags> <key>\n"
|
||||
"memcache <delete> <key>\n"
|
||||
"memcache <increment|decrement> <key> [offset]\n"
|
||||
"memcache <flush>\n"
|
||||
"memcache <status> [verbose]\n";
|
||||
|
||||
static struct {
|
||||
memcached_st *memcached;
|
||||
char *memcached_str;
|
||||
} globals;
|
||||
|
||||
static switch_status_t config_callback_memcached(switch_xml_config_item_t *data, switch_config_callback_type_t callback_type, switch_bool_t changed)
|
||||
{
|
||||
memcached_server_st *memcached_server = NULL;
|
||||
memcached_st *newmemcached = NULL;
|
||||
memcached_st *oldmemcached = NULL;
|
||||
char *memcached_str = NULL;
|
||||
memcached_return rc;
|
||||
unsigned int servercount;
|
||||
|
||||
if ((callback_type == CONFIG_LOAD || callback_type == CONFIG_RELOAD) && changed) {
|
||||
memcached_str = *((char**)data->ptr);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "memcached data: %s\n", memcached_str);
|
||||
|
||||
/* initialize main ptr */
|
||||
memcached_server = memcached_servers_parse(memcached_str);
|
||||
if (!memcached_server) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to initialize memcached data structure (server_list).\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((servercount = memcached_server_list_count(memcached_server)) == 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No memcache servers defined. Server string: %s.\n", memcached_str);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%u servers defined.\n", servercount);
|
||||
}
|
||||
|
||||
/* setup memcached */
|
||||
newmemcached = memcached_create(NULL);
|
||||
if (!newmemcached) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to initialize memcached data structure (memcached_st).\n");
|
||||
goto error;
|
||||
}
|
||||
rc = memcached_server_push(newmemcached, memcached_server);
|
||||
if (rc != MEMCACHED_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memcache error adding server list: %s\n", memcached_strerror(newmemcached, rc));
|
||||
goto error;
|
||||
}
|
||||
/* memcached_behavior_set(newmemcached, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); */
|
||||
|
||||
/* swap pointers */
|
||||
oldmemcached = globals.memcached;
|
||||
globals.memcached = newmemcached;
|
||||
newmemcached = NULL;
|
||||
}
|
||||
|
||||
if (memcached_server) {
|
||||
memcached_server_list_free(memcached_server);
|
||||
}
|
||||
if (newmemcached) {
|
||||
memcached_free(newmemcached);
|
||||
}
|
||||
if (oldmemcached) {
|
||||
memcached_free(oldmemcached);
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
error:
|
||||
if (memcached_server) {
|
||||
memcached_server_list_free(memcached_server);
|
||||
}
|
||||
if (newmemcached) {
|
||||
memcached_free(newmemcached);
|
||||
}
|
||||
if (oldmemcached) {
|
||||
memcached_free(oldmemcached);
|
||||
}
|
||||
return SWITCH_STATUS_GENERR;
|
||||
}
|
||||
|
||||
static switch_xml_config_string_options_t config_opt_memcache_servers = {NULL, 0, ".*"}; /* anything is ok here */
|
||||
|
||||
static switch_xml_config_item_t instructions[] = {
|
||||
/* parameter name type reloadable pointer default value options structure */
|
||||
SWITCH_CONFIG_ITEM_CALLBACK("memcache-servers", SWITCH_CONFIG_STRING, CONFIG_REQUIRED | CONFIG_RELOAD, &globals.memcached_str, NULL, config_callback_memcached, &config_opt_memcache_servers,
|
||||
"host,host:port,host", "List of memcached servers."),
|
||||
SWITCH_CONFIG_ITEM_END()
|
||||
};
|
||||
|
||||
static switch_status_t do_config(switch_bool_t reload)
|
||||
{
|
||||
switch_xml_t cfg, xml, settings;
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
|
||||
if (!(xml = switch_xml_open_cfg("memcache.conf", &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not open memcache.conf\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
if ((settings = switch_xml_child(cfg, "settings"))) {
|
||||
if (switch_xml_config_parse(switch_xml_child(settings, "param"), 0, instructions) == SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE,"Config parsed ok!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (xml) {
|
||||
switch_xml_free(xml);
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_API(memcache_function)
|
||||
{
|
||||
char *argv[5] = { 0 };
|
||||
int argc;
|
||||
char *subcmd = NULL;
|
||||
char *key = NULL;
|
||||
char *val = NULL;
|
||||
char *expires_str = NULL;
|
||||
char *flags_str = NULL;
|
||||
char *mydata = NULL;
|
||||
size_t string_length = 0;
|
||||
time_t expires = 0;
|
||||
uint32_t flags = 0;
|
||||
unsigned int server_count = 0;
|
||||
|
||||
memcached_return rc;
|
||||
memcached_st *memcached = NULL;
|
||||
memcached_stat_st *stat = NULL;
|
||||
memcached_server_st *server_list;
|
||||
|
||||
if (switch_strlen_zero(cmd)) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
mydata = strdup(cmd);
|
||||
if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
|
||||
if (argc < 1) {
|
||||
goto usage;
|
||||
}
|
||||
|
||||
/* clone memcached struct so we're thread safe */
|
||||
memcached = memcached_clone(NULL, globals.memcached);
|
||||
if (!memcached) {
|
||||
stream->write_function(stream, "-ERR Error cloning memcached object\n");
|
||||
}
|
||||
|
||||
subcmd = argv[0];
|
||||
|
||||
if ((!strcasecmp(subcmd, "set") || !strcasecmp(subcmd, "replace") || !strcasecmp(subcmd, "add")) && argc > 2) {
|
||||
key = argv[1];
|
||||
val = argv[2];
|
||||
|
||||
if(argc > 3) {
|
||||
expires_str = argv[3];
|
||||
expires = (time_t)strtol(expires_str, NULL, 10);
|
||||
}
|
||||
if(argc > 4) {
|
||||
flags_str = argv[4];
|
||||
flags = (uint32_t)strtol(flags_str, NULL, 16);
|
||||
}
|
||||
if (!strcasecmp(subcmd, "set")) {
|
||||
rc = memcached_set(memcached, key, strlen(key), val, strlen(val), expires, flags);
|
||||
} else if (!strcasecmp(subcmd, "replace")) {
|
||||
rc = memcached_replace(memcached, key, strlen(key), val, strlen(val), expires, flags);
|
||||
} else if (!strcasecmp(subcmd, "add")) {
|
||||
rc = memcached_add(memcached, key, strlen(key), val, strlen(val), expires, flags);
|
||||
}
|
||||
|
||||
if (rc == MEMCACHED_SUCCESS) {
|
||||
stream->write_function(stream, "+OK\n");
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Error while running command %s: %s\n", subcmd, memcached_strerror(memcached, rc));
|
||||
}
|
||||
} else if (!strcasecmp(subcmd, "get") && argc > 1) {
|
||||
key = argv[1];
|
||||
|
||||
val = memcached_get(memcached, key, strlen(key), &string_length, &flags, &rc);
|
||||
if (rc == MEMCACHED_SUCCESS) {
|
||||
stream->write_function(stream, "%.*s", (int)string_length, val);
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Error while running command %s: %s\n", subcmd, memcached_strerror(memcached, rc));
|
||||
}
|
||||
} else if (!strcasecmp(subcmd, "getflags") && argc > 1) {
|
||||
key = argv[1];
|
||||
|
||||
val = memcached_get(memcached, key, strlen(key), &string_length, &flags, &rc);
|
||||
if (rc == MEMCACHED_SUCCESS) {
|
||||
stream->write_function(stream, "%x", flags);
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Error while running command %s: %s\n", subcmd, memcached_strerror(memcached, rc));
|
||||
}
|
||||
} else if ((!strcasecmp(subcmd, "increment") || !strcasecmp(subcmd, "decrement")) && argc > 1) {
|
||||
key = argv[1];
|
||||
uint64_t ivalue;
|
||||
unsigned int offset = 1;
|
||||
if(argc > 2) {
|
||||
offset = (unsigned int)strtol(argv[2], NULL, 10);
|
||||
}
|
||||
if (!strcasecmp(subcmd, "increment")) {
|
||||
rc = memcached_increment(memcached, key, strlen(key), offset, &ivalue);
|
||||
} else if (!strcasecmp(subcmd, "decrement")) {
|
||||
rc = memcached_decrement(memcached, key, strlen(key), offset, &ivalue);
|
||||
}
|
||||
if (rc == MEMCACHED_SUCCESS) {
|
||||
stream->write_function(stream, "%ld", ivalue);
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Error while running command %s %s: %s\n", subcmd, key, memcached_strerror(memcached, rc));
|
||||
}
|
||||
} else if (!strcasecmp(subcmd, "delete") && argc > 1) {
|
||||
key = argv[1];
|
||||
if(argc > 2) {
|
||||
expires_str = argv[3];
|
||||
expires = (time_t)strtol(expires_str, NULL, 10);
|
||||
}
|
||||
rc = memcached_delete(memcached, key, strlen(key), expires);
|
||||
if (rc == MEMCACHED_SUCCESS) {
|
||||
stream->write_function(stream, "+OK\n", key);
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Error while running command %s %s: %s\n", subcmd, key, memcached_strerror(memcached, rc));
|
||||
}
|
||||
} else if (!strcasecmp(subcmd, "flush")) {
|
||||
if(argc > 1) {
|
||||
expires_str = argv[3];
|
||||
expires = (time_t)strtol(expires_str, NULL, 10);
|
||||
}
|
||||
rc = memcached_flush(memcached, expires);
|
||||
if (rc == MEMCACHED_SUCCESS) {
|
||||
stream->write_function(stream, "+OK\n", key);
|
||||
} else {
|
||||
stream->write_function(stream, "-ERR Error while running command %s : %s\n", subcmd, memcached_strerror(memcached, rc));
|
||||
}
|
||||
} else if (!strcasecmp(subcmd, "status")) {
|
||||
switch_bool_t verbose = SWITCH_FALSE;
|
||||
|
||||
if (argc > 1) {
|
||||
if (!strcasecmp(argv[1], "verbose")) {
|
||||
verbose = SWITCH_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
stream->write_function(stream, "Lib version: %s\n", memcached_lib_version());
|
||||
stat = memcached_stat(memcached, NULL, &rc);
|
||||
if (rc != MEMCACHED_SUCCESS && rc != MEMCACHED_SOME_ERRORS) {
|
||||
stream->write_function(stream, "-ERR Error communicating with servers (%s)\n", memcached_strerror(memcached, rc));
|
||||
}
|
||||
server_list = memcached_server_list(memcached);
|
||||
server_count = memcached_server_count(memcached);
|
||||
stream->write_function(stream, "Servers: %d\n", server_count);
|
||||
for (int x=0; x < server_count; x++) {
|
||||
stream->write_function(stream, " %s (%u)\n", memcached_server_name(memcached, server_list[x]), memcached_server_port(memcached, server_list[x]));
|
||||
if (verbose == SWITCH_TRUE) {
|
||||
char **list;
|
||||
char **ptr;
|
||||
char *value;
|
||||
memcached_return rc2;
|
||||
|
||||
list = memcached_stat_get_keys(memcached, &stat[x], &rc);
|
||||
for (ptr = list; *ptr; ptr++) {
|
||||
value = memcached_stat_get_value(memcached, &stat[x], *ptr, &rc2);
|
||||
stream->write_function(stream, " %s: %s\n", *ptr, value);
|
||||
switch_safe_free(value);
|
||||
}
|
||||
switch_safe_free(list);
|
||||
stream->write_function(stream, "\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (memcached) {
|
||||
memcached_quit(memcached);
|
||||
memcached_free(memcached);
|
||||
}
|
||||
switch_safe_free(mydata);
|
||||
switch_safe_free(stat);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
usage:
|
||||
if (memcached) {
|
||||
memcached_quit(memcached);
|
||||
memcached_free(memcached);
|
||||
}
|
||||
switch_safe_free(mydata);
|
||||
stream->write_function(stream, "-ERR\n%s\n", SYNTAX);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Macro expands to: switch_status_t mod_memcache_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_memcache_load)
|
||||
{
|
||||
switch_api_interface_t *api_interface;
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
do_config(SWITCH_FALSE);
|
||||
|
||||
SWITCH_ADD_API(api_interface, "memcache", "Memcache API", memcache_function, "syntax");
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the system shuts down
|
||||
Macro expands to: switch_status_t mod_memcache_shutdown() */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_memcache_shutdown)
|
||||
{
|
||||
/* Cleanup dynamically allocated config settings */
|
||||
switch_xml_config_cleanup(instructions);
|
||||
if (globals.memcached) {
|
||||
memcached_free(globals.memcached);
|
||||
}
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
If it exists, this is called in it's own thread when the module-load completes
|
||||
If it returns anything but SWITCH_STATUS_TERM it will be called again automatically
|
||||
Macro expands to: switch_status_t mod_memcache_runtime()
|
||||
SWITCH_MODULE_RUNTIME_FUNCTION(mod_memcache_runtime)
|
||||
{
|
||||
while(looping)
|
||||
{
|
||||
switch_cond_next();
|
||||
}
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
*/
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user