2 * Copyright (C) 2009, 2013 Henning Westerholt
3 * Copyright (C) 2013 Charles Chance, sipcentric.com
5 * This file is part of Kamailio, a free SIP server.
7 * Kamailio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version
12 * Kamailio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, see <http://www.gnu.org/licenses/>.
23 * \brief memcached module
26 #include "memcached.h"
29 #include "../../core/sr_module.h"
30 #include "../../core/mem/mem.h"
31 #include "../../core/dprint.h"
32 #include "../../core/ut.h"
33 #include "../../core/str.h"
40 char* mcd_srv_str = "localhost:11211";
41 /*! cache (default) expire time in seconds */
42 unsigned int mcd_expire = 0;
43 /*! cache storage mode, set or add */
44 unsigned int mcd_mode = 0;
45 /*! server timeout in ms*/
46 unsigned int mcd_timeout = 5000;
47 /*! Internal or system memory manager, default is system */
48 unsigned int mcd_memory = 0;
49 /*! stringify all values retrieved from memcached, default false */
50 unsigned int mcd_stringify = 0;
51 /*! memcached handle */
52 struct memcached_st *memcached_h;
53 /*! memcached server list */
54 struct memcached_server_st *servers;
57 static int mod_init(void);
59 static void mod_destroy(void);
63 * Exported pseudo-variables
65 static pv_export_t mod_pvs[] = {
66 { {"mct", sizeof("mct")-1}, PVT_OTHER, pv_get_mcd_value, pv_set_mcd_value,
67 pv_parse_mcd_name, 0, 0, 0 },
68 { {"mcinc", sizeof("mcinc")-1}, PVT_OTHER, pv_get_mcd_value, pv_inc_mcd_value,
69 pv_parse_mcd_name, 0, 0, 0 },
70 { {"mcdec", sizeof("mcdec")-1}, PVT_OTHER, pv_get_mcd_value, pv_dec_mcd_value,
71 pv_parse_mcd_name, 0, 0, 0 },
72 { {"mctex", sizeof("mctex")-1}, PVT_OTHER, pv_get_null, pv_set_mcd_expire,
73 pv_parse_mcd_name, 0, 0, 0 },
74 { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
81 static param_export_t params[] = {
82 {"servers", PARAM_STRING, &mcd_srv_str },
83 {"expire", INT_PARAM, &mcd_expire },
84 {"timeout", INT_PARAM, &mcd_timeout },
85 {"mode", INT_PARAM, &mcd_mode },
86 {"memory", INT_PARAM, &mcd_memory },
87 {"stringify", INT_PARAM, &mcd_stringify },
95 struct module_exports exports = {
112 * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
113 * \param mem freed memory
114 * \note pkg_free does not allow NULL pointer as standard free, therefore we check it here
117 static inline void mcd_free(memcached_st *ptr, void *mem, void *context) {
123 * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
124 * \param mem freed memory
125 * \note pkg_free does not allow NULL pointer as standard free, therefore we check it here
128 static inline void mcd_free_compat(memcached_st *ptr, void *mem) {
135 * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
136 * \param size allocated size
137 * \return allocated memory, or NULL on failure
140 static inline void* mcd_malloc(memcached_st *ptr, const size_t size, void *context) {
141 return pkg_malloc(size);
145 * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
146 * \param size allocated size
147 * \return allocated memory, or NULL on failure
150 static inline void* mcd_malloc_compat(memcached_st *ptr, const size_t size) {
151 return pkg_malloc(size);
156 * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
157 * \param mem pointer to allocated memory
158 * \param size new size of memory area
159 * \return allocated memory, or NULL on failure
162 static inline void* mcd_realloc(memcached_st *ptr, void *mem, const size_t size, void *context) {
163 return pkg_realloc(mem, size);
167 * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
168 * \param mem pointer to allocated memory
169 * \param size new size of memory area
170 * \return allocated memory, or NULL on failure
173 static inline void* mcd_realloc_compat(memcached_st *ptr, void *mem, const size_t size) {
174 return pkg_realloc(mem, size);
179 * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
180 * \param mem pointer to allocated memory
181 * \param size new size of memory area
182 * \return allocated memory, or NULL on failure
184 * \todo this is not optimal, use internal calloc implemention which is not exported yet
186 static inline void * mcd_calloc(memcached_st *ptr, size_t nelem, const size_t elsize, void *context) {
188 tmp = pkg_malloc(nelem * elsize);
190 memset(tmp, 0, nelem * elsize);
196 * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
197 * \param mem pointer to allocated memory
198 * \param size new size of memory area
199 * \return allocated memory, or NULL on failure
201 * \todo this is not optimal, use internal calloc implemention which is not exported yet
203 static inline void * mcd_calloc_compat(memcached_st *ptr, size_t nelem, const size_t elsize) {
205 tmp = pkg_malloc(nelem * elsize);
207 memset(tmp, 0, nelem * elsize);
214 * \brief Callback to check if we could connect successfully to a server
215 * \param ptr memcached handler
216 * \param server server instance
217 * \param context context for callback
218 * \return MEMCACHED_SUCCESS on success, MEMCACHED_CONNECTION_FAILURE on failure
220 static inline memcached_server_fn mcd_check_connection(const memcached_st *ptr, memcached_server_instance_st my_server, void *context) {
221 if (my_server->fd < 0) {
222 return MEMCACHED_CONNECTION_FAILURE;
224 return MEMCACHED_SUCCESS;
229 * \brief Module initialization function
230 * \return 0 on success, -1 on failure
232 static int mod_init(void) {
234 unsigned int len = 0;
236 struct memcached_server_st *svt;
238 if ((port = strchr(mcd_srv_str, ':')) != NULL) {
240 len = strlen(mcd_srv_str) - strlen(port) - 1;
242 LM_DBG("no port definition, using default port\n");
244 len = strlen(mcd_srv_str) ;
247 server = pkg_malloc(len+1);
248 if (server == NULL) {
253 strncpy(server, mcd_srv_str, len);
256 memcached_h = memcached_create(NULL);
257 if (memcached_h == NULL) {
258 LM_ERR("could not create memcached structure\n");
262 LM_DBG("allocated new server handle at %p", memcached_h);
264 if (mcd_memory == 1) {
265 LM_INFO("Use internal kamailio memory manager for memcached client library\n");
267 #if LIBMEMCACHED_VERSION_HEX >= 0x00038000
268 rc = memcached_set_memory_allocators(memcached_h, (memcached_malloc_fn)mcd_malloc,
269 (memcached_free_fn)mcd_free, (memcached_realloc_fn)mcd_realloc,
270 (memcached_calloc_fn)mcd_calloc, NULL);
272 rc = memcached_set_memory_allocators(memcached_h, (memcached_malloc_function)mcd_malloc_compat,
273 (memcached_free_function)mcd_free_compat, (memcached_realloc_function)mcd_realloc_compat,
274 (memcached_calloc_function)mcd_calloc_compat);
277 if (rc == MEMCACHED_SUCCESS) {
278 LM_DBG("memory manager callbacks set\n");
280 LM_ERR("memory manager callbacks not set, returned %s.\n", memcached_strerror(memcached_h, rc));
285 LM_INFO("Use system memory manager for memcached client library\n");
288 svt = memcached_server_list_append(servers, server, atoi(port), &rc);
290 LM_ERR("failed to append server\n");
292 memcached_server_list_free(servers);
300 if (memcached_behavior_set(memcached_h, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, mcd_timeout) != MEMCACHED_SUCCESS) {
301 LM_ERR("could not set server connection timeout\n");
305 rc = memcached_server_push(memcached_h, servers);
306 if (rc == MEMCACHED_SUCCESS) {
307 LM_DBG("added server list to structure\n");
309 LM_ERR("attempt to add server list to structure returned %s.\n", memcached_strerror(memcached_h, rc));
316 /** \todo FIXME logic to handle connection errors on startup
317 memcached_server_cursor(memcached_h, (const memcached_server_fn*) &mcd_check_connection, NULL, 1);
320 LM_INFO("libmemcached version is %s\n", memcached_lib_version());
326 * \brief Module shutdown function
328 static void mod_destroy(void) {
330 memcached_server_list_free(servers);
332 /* Crash on shutdown with internal memory manager, even if we disable the mm callbacks */
333 if (mcd_memory != 1 && memcached_h != NULL)
334 memcached_free(memcached_h);