memcached.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. * \file
  22. * \brief memcached module
  23. */
  24. #include "memcached.h"
  25. #include "mcd_var.h"
  26. #include "../../sr_module.h"
  27. #include "../../mem/mem.h"
  28. #include "../../dprint.h"
  29. #include "../../ut.h"
  30. #include "../../str.h"
  31. MODULE_VERSION
  32. /*! server string */
  33. char* mcd_srv_str = "localhost:11211";
  34. /*! cache (default) expire time in seconds */
  35. unsigned int mcd_expire = 0;
  36. /*! cache storage mode, set or add */
  37. unsigned int mcd_mode = 0;
  38. /*! server timeout in ms*/
  39. unsigned int mcd_timeout = 5000;
  40. /*! Internal or system memory manager, default is system */
  41. unsigned int mcd_memory = 0;
  42. /*! stringify all values retrieved from memcached, default false */
  43. unsigned int mcd_stringify = 0;
  44. /*! memcached handle */
  45. struct memcached_st *memcached_h;
  46. /*! memcached server list */
  47. struct memcached_server_st *servers;
  48. static int mod_init(void);
  49. static void mod_destroy(void);
  50. /*!
  51. * Exported pseudo-variables
  52. */
  53. static pv_export_t mod_pvs[] = {
  54. { {"mct", sizeof("mct")-1}, PVT_OTHER, pv_get_mcd_value, pv_set_mcd_value,
  55. pv_parse_mcd_name, 0, 0, 0 },
  56. { {"mcinc", sizeof("mcinc")-1}, PVT_OTHER, pv_get_mcd_value, pv_inc_mcd_value,
  57. pv_parse_mcd_name, 0, 0, 0 },
  58. { {"mcdec", sizeof("mcdec")-1}, PVT_OTHER, pv_get_mcd_value, pv_dec_mcd_value,
  59. pv_parse_mcd_name, 0, 0, 0 },
  60. { {"mctex", sizeof("mctex")-1}, PVT_OTHER, pv_get_null, pv_set_mcd_expire,
  61. pv_parse_mcd_name, 0, 0, 0 },
  62. { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
  63. };
  64. /*!
  65. * Exported parameters
  66. */
  67. static param_export_t params[] = {
  68. {"servers", PARAM_STRING, &mcd_srv_str },
  69. {"expire", INT_PARAM, &mcd_expire },
  70. {"timeout", INT_PARAM, &mcd_timeout },
  71. {"mode", INT_PARAM, &mcd_mode },
  72. {"memory", INT_PARAM, &mcd_memory },
  73. {"stringify", INT_PARAM, &mcd_stringify },
  74. {0, 0, 0}
  75. };
  76. /*!
  77. * Module interface
  78. */
  79. struct module_exports exports = {
  80. "memcached",
  81. DEFAULT_DLFLAGS,
  82. 0,
  83. params,
  84. 0,
  85. 0,
  86. mod_pvs,
  87. 0,
  88. mod_init,
  89. 0,
  90. mod_destroy,
  91. 0
  92. };
  93. /*!
  94. * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
  95. * \param mem freed memory
  96. * \note pkg_free does not allow NULL pointer as standard free, therefore we check it here
  97. * \see pkg_free
  98. */
  99. static inline void mcd_free(memcached_st *ptr, void *mem, void *context) {
  100. if (mem)
  101. pkg_free(mem);
  102. }
  103. /*!
  104. * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
  105. * \param mem freed memory
  106. * \note pkg_free does not allow NULL pointer as standard free, therefore we check it here
  107. * \see pkg_free
  108. */
  109. static inline void mcd_free_compat(memcached_st *ptr, void *mem) {
  110. if (mem)
  111. pkg_free(mem);
  112. }
  113. /*!
  114. * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
  115. * \param size allocated size
  116. * \return allocated memory, or NULL on failure
  117. * \see pkg_malloc
  118. */
  119. static inline void* mcd_malloc(memcached_st *ptr, const size_t size, void *context) {
  120. return pkg_malloc(size);
  121. }
  122. /*!
  123. * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
  124. * \param size allocated size
  125. * \return allocated memory, or NULL on failure
  126. * \see pkg_malloc
  127. */
  128. static inline void* mcd_malloc_compat(memcached_st *ptr, const size_t size) {
  129. return pkg_malloc(size);
  130. }
  131. /*!
  132. * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
  133. * \param mem pointer to allocated memory
  134. * \param size new size of memory area
  135. * \return allocated memory, or NULL on failure
  136. * \see pkg_realloc
  137. */
  138. static inline void* mcd_realloc(memcached_st *ptr, void *mem, const size_t size, void *context) {
  139. return pkg_realloc(mem, size);
  140. }
  141. /*!
  142. * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
  143. * \param mem pointer to allocated memory
  144. * \param size new size of memory area
  145. * \return allocated memory, or NULL on failure
  146. * \see pkg_realloc
  147. */
  148. static inline void* mcd_realloc_compat(memcached_st *ptr, void *mem, const size_t size) {
  149. return pkg_realloc(mem, size);
  150. }
  151. /*!
  152. * \brief Wrapper functions around our internal memory management for libmemcached (version >= 0.38) callback
  153. * \param mem pointer to allocated memory
  154. * \param size new size of memory area
  155. * \return allocated memory, or NULL on failure
  156. * \see pkg_malloc
  157. * \todo this is not optimal, use internal calloc implemention which is not exported yet
  158. */
  159. static inline void * mcd_calloc(memcached_st *ptr, size_t nelem, const size_t elsize, void *context) {
  160. void* tmp = NULL;
  161. tmp = pkg_malloc(nelem * elsize);
  162. if (tmp != NULL) {
  163. memset(tmp, 0, nelem * elsize);
  164. }
  165. return tmp;
  166. }
  167. /*!
  168. * \brief Wrapper functions around our internal memory management for libmemcached (version < 0.38) callback
  169. * \param mem pointer to allocated memory
  170. * \param size new size of memory area
  171. * \return allocated memory, or NULL on failure
  172. * \see pkg_malloc
  173. * \todo this is not optimal, use internal calloc implemention which is not exported yet
  174. */
  175. static inline void * mcd_calloc_compat(memcached_st *ptr, size_t nelem, const size_t elsize) {
  176. void* tmp = NULL;
  177. tmp = pkg_malloc(nelem * elsize);
  178. if (tmp != NULL) {
  179. memset(tmp, 0, nelem * elsize);
  180. }
  181. return tmp;
  182. }
  183. /**
  184. * \brief Callback to check if we could connect successfully to a server
  185. * \param ptr memcached handler
  186. * \param server server instance
  187. * \param context context for callback
  188. * \return MEMCACHED_SUCCESS on success, MEMCACHED_CONNECTION_FAILURE on failure
  189. * \todo FIXME
  190. static inline memcached_server_fn mcd_check_connection(const memcached_st *ptr, memcached_server_instance_st my_server, void *context) {
  191. if (my_server->fd < 0) {
  192. return MEMCACHED_CONNECTION_FAILURE;
  193. }
  194. return MEMCACHED_SUCCESS;
  195. }
  196. */
  197. /*!
  198. * \brief Module initialization function
  199. * \return 0 on success, -1 on failure
  200. */
  201. static int mod_init(void) {
  202. char *server, *port;
  203. unsigned int len = 0;
  204. memcached_return rc;
  205. if ((port = strchr(mcd_srv_str, ':')) != NULL) {
  206. port = port + 1;
  207. len = strlen(mcd_srv_str) - strlen(port) - 1;
  208. } else {
  209. LM_DBG("no port definition, using default port\n");
  210. port = "11211";
  211. len = strlen(mcd_srv_str) ;
  212. }
  213. server = pkg_malloc(len);
  214. if (server == NULL) {
  215. PKG_MEM_ERROR;
  216. return -1;
  217. }
  218. strncpy(server, mcd_srv_str, len);
  219. server[len] = '\0';
  220. memcached_h = memcached_create(NULL);
  221. if (memcached_h == NULL) {
  222. LM_ERR("could not create memcached structure\n");
  223. return -1;
  224. }
  225. LM_DBG("allocated new server handle at %p", memcached_h);
  226. if (mcd_memory == 1) {
  227. LM_INFO("Use internal kamailio memory manager for memcached client library\n");
  228. #if LIBMEMCACHED_VERSION_HEX >= 0x00038000
  229. rc = memcached_set_memory_allocators(memcached_h, (memcached_malloc_fn)mcd_malloc,
  230. (memcached_free_fn)mcd_free, (memcached_realloc_fn)mcd_realloc,
  231. (memcached_calloc_fn)mcd_calloc, NULL);
  232. #else
  233. rc = memcached_set_memory_allocators(memcached_h, (memcached_malloc_function)mcd_malloc_compat,
  234. (memcached_free_function)mcd_free_compat, (memcached_realloc_function)mcd_realloc_compat,
  235. (memcached_calloc_function)mcd_calloc_compat);
  236. #endif
  237. if (rc == MEMCACHED_SUCCESS) {
  238. LM_DBG("memory manager callbacks set\n");
  239. } else {
  240. LM_ERR("memory manager callbacks not set, returned %s.\n", memcached_strerror(memcached_h, rc));
  241. return -1;
  242. }
  243. } else {
  244. LM_INFO("Use system memory manager for memcached client library\n");
  245. }
  246. servers = memcached_server_list_append(servers, server, atoi(port), &rc);
  247. if (memcached_behavior_set(memcached_h, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, mcd_timeout) != MEMCACHED_SUCCESS) {
  248. LM_ERR("could not set server connection timeout\n");
  249. return -1;
  250. }
  251. rc = memcached_server_push(memcached_h, servers);
  252. if (rc == MEMCACHED_SUCCESS) {
  253. LM_DBG("added server list to structure\n");
  254. } else {
  255. LM_ERR("attempt to add server list to structure returned %s.\n", memcached_strerror(memcached_h, rc));
  256. return -1;
  257. }
  258. pkg_free(server);
  259. /** \todo FIXME logic to handle connection errors on startup
  260. memcached_server_cursor(memcached_h, (const memcached_server_fn*) &mcd_check_connection, NULL, 1);
  261. */
  262. LM_INFO("libmemcached version is %s\n", memcached_lib_version());
  263. return 0;
  264. }
  265. /*!
  266. * \brief Module shutdown function
  267. */
  268. static void mod_destroy(void) {
  269. if (servers != NULL)
  270. memcached_server_list_free(servers);
  271. /* Crash on shutdown with internal memory manager, even if we disable the mm callbacks */
  272. if (mcd_memory != 1 && memcached_h != NULL)
  273. memcached_free(memcached_h);
  274. }