The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_cache_memcached.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: ea2657f11817f6d23f16e5bf84596b76db7d71ac $
19 * @file rlm_cache_memcached.c
20 * @brief memcached based cache.
21 *
22 * @copyright 2014 The FreeRADIUS server project
23 */
24
25#define LOG_PREFIX "cache - memcached"
26
27#include <libmemcached/memcached.h>
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/util/debug.h>
32#include <freeradius-devel/util/slab.h>
33#include <freeradius-devel/util/value.h>
34
35#include "../../rlm_cache.h"
36#include "../../serialize.h"
37
38typedef struct {
39 memcached_st *handle;
41
44
45typedef struct {
46 char const *options; //!< Connection options
51
52typedef struct {
54 memcached_slab_list_t *slab;
56
61
62static const conf_parser_t driver_config[] = {
63 { FR_CONF_OFFSET("options", rlm_cache_memcached_t, options), .dflt = "--SERVER=localhost" },
64 { FR_CONF_OFFSET("timeout", rlm_cache_memcached_t, timeout), .dflt = "3.0" },
67};
68
69/** Free a connection handle
70 *
71 * @param mandle to free.
72 */
74{
75 if (mandle->handle) memcached_free(mandle->handle);
76
77 return 0;
78}
79
80/** Create a new memcached handle
81 *
82 */
84{
85 rlm_cache_memcached_t *driver = talloc_get_type_abort(uctx, rlm_cache_memcached_t);
86 memcached_st *sandle;
87 memcached_return_t ret;
88
89 sandle = memcached(driver->options, talloc_array_length(driver->options) -1);
90 if (!sandle) {
91 ERROR("Failed creating memcached connection");
92 return -1;
93 }
94
95 ret = memcached_behavior_set(sandle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, fr_time_delta_to_msec(driver->timeout));
96 if (ret != MEMCACHED_SUCCESS) {
97 ERROR("%s: %s", memcached_strerror(sandle, ret), memcached_last_error_message(sandle));
98 error:
99 memcached_free(sandle);
100 return -1;
101 }
102
103 ret = memcached_version(sandle);
104 if (ret != MEMCACHED_SUCCESS) {
105 ERROR("%s: %s", memcached_strerror(sandle, ret), memcached_last_error_message(sandle));
106 goto error;
107 }
108
109 mandle->handle = sandle;
110 talloc_set_destructor(mandle, _mod_conn_free);
111
112 return 0;
113}
114
115/** Create a new rlm_cache_memcached instance
116 *
117 * @param[in] mctx Data required for instantiation.
118 * @return
119 * - 0 on success.
120 * - -1 on failure.
121 */
122static int mod_instantiate(module_inst_ctx_t const *mctx)
123{
124 rlm_cache_memcached_t *driver = talloc_get_type_abort(mctx->mi->data, rlm_cache_memcached_t);
125 memcached_return_t ret;
126 char buffer[256];
127 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->parent->data, rlm_cache_t);
128
129 ret = libmemcached_check_configuration(driver->options, talloc_array_length(driver->options) -1,
130 buffer, sizeof(buffer));
131 if (ret != MEMCACHED_SUCCESS) {
132 ERROR("%s", buffer);
133 return -1;
134 }
135
136 if (inst->config.max_entries > 0) {
137 ERROR("max_entries is not supported by this driver");
138 return -1;
139 }
140
141 driver->mi = mctx->mi;
142 return 0;
143}
144
145static int mod_load(void)
146{
147 INFO("%s", memcached_lib_version());
148 return 0;
149}
150
151/** Locate a cache entry in memcached
152 *
153 * @copydetails cache_entry_free_t
154 */
156{
157 talloc_free(c);
158}
159
160/** Locate a cache entry in memcached
161 *
162 * @copydetails cache_entry_find_t
163 */
165 UNUSED rlm_cache_config_t const *config, UNUSED void *instance,
166 request_t *request, void *handle, fr_value_box_t const *key)
167{
168 rlm_cache_memcached_handle_t *mandle = handle;
169
170 memcached_return_t mret;
171 size_t len;
172 int ret;
173 uint32_t flags;
174
175 char *from_store;
176
178
179 from_store = memcached_get(mandle->handle, (char const *)key->vb_strvalue, key->vb_length, &len, &flags, &mret);
180 if (!from_store) {
181 if (mret == MEMCACHED_NOTFOUND) return CACHE_MISS;
182
183 RERROR("Failed retrieving entry: %s: %s", memcached_strerror(mandle->handle, mret),
184 memcached_last_error_message(mandle->handle));
185
186 return memcached_fatal(mret) ? CACHE_RECONNECT : CACHE_ERROR;
187 }
188 RDEBUG2("Retrieved %zu bytes from memcached", len);
189 RDEBUG2("%s", from_store);
190
191 MEM(c = talloc_zero(NULL, rlm_cache_entry_t));
192 map_list_init(&c->maps);
193 ret = cache_deserialize(request, c, request->dict, from_store, len);
194 free(from_store);
195 if (ret < 0) {
196 RPERROR("Invalid entry");
197 error:
198 talloc_free(c);
199 return CACHE_ERROR;
200 }
201 if (unlikely(fr_value_box_copy(c, &c->key, key) < 0)) {
202 RERROR("Failed copying key");
203 goto error;
204 }
205
206 *out = c;
207
208 return CACHE_OK;
209}
210
211/** Insert a new entry into the data store
212 *
213 * @copydetails cache_entry_insert_t
214 */
216 request_t *request, void *handle, const rlm_cache_entry_t *c)
217{
218 rlm_cache_memcached_handle_t *mandle = handle;
219
220 memcached_return_t ret;
221
222 TALLOC_CTX *pool;
223 char *to_store;
224
225 pool = talloc_pool(NULL, 1024);
226 if (!pool) return CACHE_ERROR;
227
228 if (cache_serialize(pool, &to_store, c) < 0) {
229 talloc_free(pool);
230
231 return CACHE_ERROR;
232 }
233
234 ret = memcached_set(mandle->handle, (char const *)c->key.vb_strvalue, c->key.vb_length,
235 to_store ? to_store : "",
236 to_store ? talloc_array_length(to_store) - 1 : 0, fr_unix_time_to_sec(c->expires), 0);
237 talloc_free(pool);
238 if (ret != MEMCACHED_SUCCESS) {
239 RERROR("Failed storing entry: %s: %s", memcached_strerror(mandle->handle, ret),
240 memcached_last_error_message(mandle->handle));
241
242 return memcached_fatal(ret) ? CACHE_RECONNECT : CACHE_ERROR;
243 }
244
245 return CACHE_OK;
246}
247
248/** Call delete the cache entry from memcached
249 *
250 * @copydetails cache_entry_expire_t
251 */
253 request_t *request, void *handle, fr_value_box_t const *key)
254{
255 rlm_cache_memcached_handle_t *mandle = handle;
256
257 memcached_return_t ret;
258
259 ret = memcached_delete(mandle->handle, (char const *)key->vb_strvalue, key->vb_length, 0);
260 switch (ret) {
261 case MEMCACHED_SUCCESS:
262 return CACHE_OK;
263
264 case MEMCACHED_DATA_DOES_NOT_EXIST:
265 return CACHE_MISS;
266
267 default:
268 RERROR("Failed deleting entry: %s", memcached_last_error_message(mandle->handle));
269 return memcached_fatal(ret) ? CACHE_RECONNECT : CACHE_ERROR;
270 }
271}
272
273/** Get a memcached handle
274 *
275 * @copydetails cache_acquire_t
276 */
277static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *instance,
278 UNUSED request_t *request)
279{
280 rlm_cache_memcached_t *driver = instance;
281 rlm_cache_handle_t *mandle;
283
284 mandle = memcached_slab_reserve(t->slab);
285 if (!mandle) {
286 *handle = NULL;
287 return -1;
288 }
289 *handle = mandle;
290
291 return 0;
292}
293
294/** Release a memcached handle
295 *
296 * @copydetails cache_release_t
297 */
298static void mod_conn_release(UNUSED rlm_cache_config_t const *config, UNUSED void *instance,
299 UNUSED request_t *request, rlm_cache_handle_t *handle)
300{
301 memcached_slab_release(handle);
302}
303
304/** Reconnect a memcached handle
305 *
306 * @copydetails cache_reconnect_t
307 */
308static int mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *instance,
309 UNUSED request_t *request)
310{
311 rlm_cache_memcached_t *driver = instance;
313 rlm_cache_handle_t *mandle;
314
315 talloc_free(*handle);
316 mandle = memcached_slab_reserve(t->slab);
317 if (!mandle) {
318 *handle = NULL;
319 return -1;
320 }
321 *handle = mandle;
322
323 return 0;
324}
325
327{
328 rlm_cache_memcached_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_memcached_t);
329 rlm_cache_memcached_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_cache_memcached_thread_t);
330
331 t->inst = inst;
332 if (!(t->slab = memcached_slab_list_alloc(t, mctx->el, &inst->reuse, memcached_conn_init, NULL,
333 UNCONST(void *, inst), false, false))) {
334 ERROR("Connection handle pool instantiation failed");
335 return -1;
336 }
337
338 return 0;
339}
340
342{
343 rlm_cache_memcached_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_cache_memcached_thread_t);
344 talloc_free(t->slab);
345 return 0;
346}
347
350 .common = {
351 .magic = MODULE_MAGIC_INIT,
352 .name = "cache_memcached",
355
356 .onload = mod_load,
358 .thread_inst_size = sizeof(rlm_cache_memcached_thread_t),
359 .thread_instantiate = mod_thread_instantiate,
360 .thread_detach = mod_thread_detach,
361 },
362
363 .free = cache_entry_free,
364
365 .find = cache_entry_find,
366 .insert = cache_entry_insert,
367 .expire = cache_entry_expire,
368
369 .acquire = mod_conn_get,
370 .release = mod_conn_release,
371 .reconnect = mod_conn_reconnect
372};
static int const char char buffer[256]
Definition acutest.h:576
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:268
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:297
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
free(array)
#define RERROR(fmt,...)
Definition log.h:298
#define RPERROR(fmt,...)
Definition log.h:302
talloc_free(reap)
unsigned int uint32_t
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
static const conf_parser_t config[]
Definition base.c:183
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define INFO(fmt,...)
Definition radict.c:54
fr_value_box_t key
Key used to identify entry.
Definition rlm_cache.h:73
map_list_t maps
Head of the maps list.
Definition rlm_cache.h:78
module_t common
Common fields for all loadable modules.
Definition rlm_cache.h:258
cache_status_t
Definition rlm_cache.h:39
@ CACHE_ERROR
Fatal error.
Definition rlm_cache.h:41
@ CACHE_RECONNECT
Handle needs to be reconnected.
Definition rlm_cache.h:40
@ CACHE_OK
Cache entry found/updated.
Definition rlm_cache.h:42
@ CACHE_MISS
Cache entry notfound.
Definition rlm_cache.h:43
void rlm_cache_handle_t
Definition rlm_cache.h:35
fr_unix_time_t expires
When the entry expires.
Definition rlm_cache.h:76
Configuration for the rlm_cache module.
Definition rlm_cache.h:51
Definition rlm_cache.h:72
static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, UNUSED void *instance, request_t *request, void *handle, fr_value_box_t const *key)
Call delete the cache entry from memcached.
static int _mod_conn_free(rlm_cache_memcached_handle_t *mandle)
Free a connection handle.
static int mod_load(void)
static int memcached_conn_init(rlm_cache_memcached_handle_t *mandle, void *uctx)
Create a new memcached handle.
static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, UNUSED void *instance, request_t *request, void *handle, const rlm_cache_entry_t *c)
Insert a new entry into the data store.
static void cache_entry_free(rlm_cache_entry_t *c)
Locate a cache entry in memcached.
rlm_cache_driver_t rlm_cache_memcached
memcached_slab_list_t * slab
static void mod_conn_release(UNUSED rlm_cache_config_t const *config, UNUSED void *instance, UNUSED request_t *request, rlm_cache_handle_t *handle)
Release a memcached handle.
static cache_status_t cache_entry_find(rlm_cache_entry_t **out, UNUSED rlm_cache_config_t const *config, UNUSED void *instance, request_t *request, void *handle, fr_value_box_t const *key)
Locate a cache entry in memcached.
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
static const conf_parser_t driver_config[]
rlm_cache_memcached_t const * inst
char const * options
Connection options.
static conf_parser_t reuse_memcached_config[]
static int mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *instance, UNUSED request_t *request)
Reconnect a memcached handle.
module_instance_t const * mi
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
Create a new rlm_cache_memcached instance.
static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *instance, UNUSED request_t *request)
Get a memcached handle.
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1310
int cache_serialize(TALLOC_CTX *ctx, char **out, rlm_cache_entry_t const *c)
Serialize a cache entry as a humanly readable string.
Definition serialize.c:41
int cache_deserialize(request_t *request, rlm_cache_entry_t *c, fr_dict_t const *dict, char *in, ssize_t inlen)
Converts a serialized cache entry back into a structure.
Definition serialize.c:104
size_t inst_size
Size of the module's instance data.
Definition module.h:203
void * data
Module's instance data.
Definition module.h:271
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:337
void * data
Thread specific instance data.
Definition module.h:352
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:481
Module instance data.
Definition module.h:265
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
Definition slab.h:120
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition slab.h:72
#define FR_SLAB_CONFIG_CONF_PARSER
conf_parser_t entries to populate user configurable slab values
Definition slab.h:35
Tuneable parameters for slabs.
Definition slab.h:42
eap_aka_sim_process_conf_t * inst
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition time.h:506
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition time.h:637
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition value.c:3740
static size_t char ** out
Definition value.h:997