]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add API for memcached
authorRupa Schomaker <rupa@rupa.com>
Wed, 1 Apr 2009 19:02:59 +0000 (19:02 +0000)
committerRupa Schomaker <rupa@rupa.com>
Wed, 1 Apr 2009 19:02:59 +0000 (19:02 +0000)
have to run through valgrind still

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@12871 d0543943-73ff-0310-b7d9-9358b9ac24b2

src/mod/applications/mod_memcache/Makefile [new file with mode: 0644]
src/mod/applications/mod_memcache/mod_memcache.c [new file with mode: 0755]

diff --git a/src/mod/applications/mod_memcache/Makefile b/src/mod/applications/mod_memcache/Makefile
new file mode 100644 (file)
index 0000000..68f88cf
--- /dev/null
@@ -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)
+
+
+
diff --git a/src/mod/applications/mod_memcache/mod_memcache.c b/src/mod/applications/mod_memcache/mod_memcache.c
new file mode 100755 (executable)
index 0000000..a1f6752
--- /dev/null
@@ -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
+ */