memcached: handle error cases and alloc enough for null termination
[sip-router] / src / modules / memcached / memcached.c
1 /*
2  * Copyright (C) 2009, 2013 Henning Westerholt
3  * Copyright (C) 2013 Charles Chance, sipcentric.com
4  *
5  * This file is part of Kamailio, a free SIP server.
6  *
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
11  *
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.
16  *
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/>.
19  */
20
21 /*!
22  * \file
23  * \brief memcached module
24  */
25
26 #include "memcached.h"
27 #include "mcd_var.h"
28
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"
34
35
36 MODULE_VERSION
37
38
39 /*! server string */
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;
55
56
57 static int mod_init(void);
58
59 static void mod_destroy(void);
60
61
62 /*!
63  * Exported pseudo-variables
64  */
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 }
75 };
76
77
78 /*!
79  * Exported parameters
80  */
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 },
88         {0, 0, 0}
89 };
90
91
92 /*!
93  * Module interface
94  */
95 struct module_exports exports = {
96         "memcached",
97         DEFAULT_DLFLAGS,
98         0,
99         params,
100         0,
101         0,
102         mod_pvs,
103         0,
104         mod_init,
105         0,
106         mod_destroy,
107         0
108 };
109
110
111 /*!
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
115  * \see pkg_free
116  */
117 static inline void mcd_free(memcached_st *ptr, void *mem, void *context) {
118         if (mem)
119                 pkg_free(mem);
120 }
121
122 /*!
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
126  * \see pkg_free
127  */
128  static inline void mcd_free_compat(memcached_st *ptr, void *mem) {
129         if (mem)
130                 pkg_free(mem);
131 }
132
133
134 /*!
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
138  * \see pkg_malloc
139  */
140 static inline void* mcd_malloc(memcached_st *ptr, const size_t size, void *context) {
141         return pkg_malloc(size);
142 }
143
144 /*!
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
148  * \see pkg_malloc
149  */
150  static inline void* mcd_malloc_compat(memcached_st *ptr, const size_t size) {
151         return pkg_malloc(size);
152 }
153
154
155 /*!
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
160  * \see pkg_realloc
161  */
162 static inline void* mcd_realloc(memcached_st *ptr, void *mem, const size_t size, void *context) {
163         return pkg_realloc(mem, size);
164 }
165
166 /*!
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
171  * \see pkg_realloc
172  */
173 static inline void* mcd_realloc_compat(memcached_st *ptr, void *mem, const size_t size) {
174         return pkg_realloc(mem, size);
175 }
176
177
178 /*!
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
183  * \see pkg_malloc
184  * \todo this is not optimal,   use internal calloc implemention which is not exported yet
185  */
186 static inline void * mcd_calloc(memcached_st *ptr, size_t nelem, const size_t elsize, void *context) {
187         void* tmp = NULL;
188         tmp = pkg_malloc(nelem * elsize);
189         if (tmp != NULL) {
190                 memset(tmp, 0, nelem * elsize);
191         }
192         return tmp;
193 }
194
195 /*!
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
200  * \see pkg_malloc
201  * \todo this is not optimal,   use internal calloc implemention which is not exported yet
202  */
203 static inline void * mcd_calloc_compat(memcached_st *ptr, size_t nelem, const size_t elsize) {
204         void* tmp = NULL;
205         tmp = pkg_malloc(nelem * elsize);
206         if (tmp != NULL) {
207                 memset(tmp, 0, nelem * elsize);
208         }
209         return tmp;
210 }
211
212
213 /**
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
219  * \todo FIXME
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;
223         }
224         return MEMCACHED_SUCCESS;
225 }
226 */
227
228 /*!
229  * \brief Module initialization function
230  * \return 0 on success, -1 on failure
231  */
232 static int mod_init(void) {
233         char *server, *port;
234         unsigned int len = 0;
235         memcached_return rc;
236         struct memcached_server_st *svt;
237
238         if ((port = strchr(mcd_srv_str, ':')) != NULL) {
239                 port = port + 1;
240                 len = strlen(mcd_srv_str) - strlen(port) - 1;
241         } else {
242                 LM_DBG("no port definition, using default port\n");
243                 port = "11211";
244                 len = strlen(mcd_srv_str) ;
245         }
246
247         server = pkg_malloc(len+1);
248         if (server == NULL) {
249                 PKG_MEM_ERROR;
250                 return -1;
251         }
252
253         strncpy(server, mcd_srv_str, len);
254         server[len] = '\0';
255
256         memcached_h = memcached_create(NULL);
257         if (memcached_h == NULL) {
258                 LM_ERR("could not create memcached structure\n");
259                 pkg_free(server);
260                 return -1;
261         }
262         LM_DBG("allocated new server handle at %p", memcached_h);
263
264         if (mcd_memory == 1) {
265                 LM_INFO("Use internal kamailio memory manager for memcached client library\n");
266
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);
271 #else
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);
275 #endif
276
277                 if (rc == MEMCACHED_SUCCESS) {
278                         LM_DBG("memory manager callbacks set\n");
279                 } else {
280                         LM_ERR("memory manager callbacks not set, returned %s.\n", memcached_strerror(memcached_h, rc));
281                         pkg_free(server);
282                         return -1;
283                 }
284         } else {
285                 LM_INFO("Use system memory manager for memcached client library\n");
286         }
287
288         svt = memcached_server_list_append(servers, server, atoi(port), &rc);
289         if(svt==NULL) {
290                 LM_ERR("failed to append server\n");
291                 if(servers) {
292                         memcached_server_list_free(servers);
293                         servers = NULL;
294                 }
295                 pkg_free(server);
296                 return -1;
297         }
298
299         servers = svt;
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");
302                 pkg_free(server);
303                 return -1;
304         }
305         rc = memcached_server_push(memcached_h, servers);
306         if (rc == MEMCACHED_SUCCESS) {
307                 LM_DBG("added server list to structure\n");
308         } else {
309                 LM_ERR("attempt to add server list to structure returned %s.\n", memcached_strerror(memcached_h, rc));
310                 pkg_free(server);
311                 return -1;
312         }
313
314         pkg_free(server);
315
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);
318         */
319
320         LM_INFO("libmemcached version is %s\n", memcached_lib_version());
321         return 0;
322 }
323
324
325 /*!
326  * \brief Module shutdown function
327  */
328 static void mod_destroy(void) {
329         if (servers != NULL)
330                 memcached_server_list_free(servers);
331
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);
335 }