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 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: efd9820ed814b1f04adf7c221309883ec8974f4a $
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
34#include "../../rlm_cache.h"
35#include "../../serialize.h"
36
37typedef struct {
38 memcached_st *handle;
40
43
44typedef struct {
45 char const *options; //!< Connection options
50
51typedef struct {
53 memcached_slab_list_t *slab;
55
60
61static const conf_parser_t driver_config[] = {
62 { FR_CONF_OFFSET("options", rlm_cache_memcached_t, options), .dflt = "--SERVER=localhost" },
63 { FR_CONF_OFFSET("timeout", rlm_cache_memcached_t, timeout), .dflt = "3.0" },
66};
67
68/** Free a connection handle
69 *
70 * @param mandle to free.
71 */
73{
74 if (mandle->handle) memcached_free(mandle->handle);
75
76 return 0;
77}
78
79/** Create a new memcached handle
80 *
81 */
83{
84 rlm_cache_memcached_t *driver = talloc_get_type_abort(uctx, rlm_cache_memcached_t);
85 memcached_st *sandle;
86 memcached_return_t ret;
87
88 sandle = memcached(driver->options, talloc_strlen(driver->options));
89 if (!sandle) {
90 ERROR("Failed creating memcached connection");
91 return -1;
92 }
93
94 ret = memcached_behavior_set(sandle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, fr_time_delta_to_msec(driver->timeout));
95 if (ret != MEMCACHED_SUCCESS) {
96 ERROR("%s: %s", memcached_strerror(sandle, ret), memcached_last_error_message(sandle));
97 error:
98 memcached_free(sandle);
99 return -1;
100 }
101
102 ret = memcached_version(sandle);
103 if (ret != MEMCACHED_SUCCESS) {
104 ERROR("%s: %s", memcached_strerror(sandle, ret), memcached_last_error_message(sandle));
105 goto error;
106 }
107
108 mandle->handle = sandle;
109 talloc_set_destructor(mandle, _mod_conn_free);
110
111 return 0;
112}
113
114/** Create a new rlm_cache_memcached instance
115 *
116 * @param[in] mctx Data required for instantiation.
117 * @return
118 * - 0 on success.
119 * - -1 on failure.
120 */
121static int mod_instantiate(module_inst_ctx_t const *mctx)
122{
123 rlm_cache_memcached_t *driver = talloc_get_type_abort(mctx->mi->data, rlm_cache_memcached_t);
124 memcached_return_t ret;
125 char buffer[256];
126 rlm_cache_t const *inst = talloc_get_type_abort(mctx->mi->parent->data, rlm_cache_t);
127
128 ret = libmemcached_check_configuration(driver->options, talloc_strlen(driver->options),
129 buffer, sizeof(buffer));
130 if (ret != MEMCACHED_SUCCESS) {
131 ERROR("%s", buffer);
132 return -1;
133 }
134
135 if (inst->config.max_entries > 0) {
136 ERROR("max_entries is not supported by this driver");
137 return -1;
138 }
139
140 driver->mi = mctx->mi;
141 return 0;
142}
143
144static int mod_load(void)
145{
146 INFO("%s", memcached_lib_version());
147 return 0;
148}
149
150/** Locate a cache entry in memcached
151 *
152 * @copydetails cache_entry_free_t
153 */
155{
156 talloc_free(c);
157}
158
159/** Locate a cache entry in memcached
160 *
161 * @copydetails cache_entry_find_t
162 */
164 UNUSED rlm_cache_config_t const *config, UNUSED void *instance,
165 request_t *request, void *handle, fr_value_box_t const *key)
166{
167 rlm_cache_memcached_handle_t *mandle = handle;
168
169 memcached_return_t mret;
170 size_t len;
171 int ret;
172 uint32_t flags;
173
174 char *from_store;
175
177
178 from_store = memcached_get(mandle->handle, (char const *)key->vb_strvalue, key->vb_length, &len, &flags, &mret);
179 if (!from_store) {
180 if (mret == MEMCACHED_NOTFOUND) return CACHE_MISS;
181
182 RERROR("Failed retrieving entry: %s: %s", memcached_strerror(mandle->handle, mret),
183 memcached_last_error_message(mandle->handle));
184
185 return memcached_fatal(mret) ? CACHE_RECONNECT : CACHE_ERROR;
186 }
187 RDEBUG2("Retrieved %zu bytes from memcached", len);
188 RDEBUG2("%s", from_store);
189
190 MEM(c = talloc_zero(NULL, rlm_cache_entry_t));
191 map_list_init(&c->maps);
192 ret = cache_deserialize(request, c, request->proto_dict, from_store, len);
193 free(from_store);
194 if (ret < 0) {
195 RPERROR("Invalid entry");
196 error:
197 talloc_free(c);
198 return CACHE_ERROR;
199 }
200 if (unlikely(fr_value_box_copy(c, &c->key, key) < 0)) {
201 RERROR("Failed copying key");
202 goto error;
203 }
204
205 *out = c;
206
207 return CACHE_OK;
208}
209
210/** Insert a new entry into the data store
211 *
212 * @copydetails cache_entry_insert_t
213 */
215 request_t *request, void *handle, const rlm_cache_entry_t *c)
216{
217 rlm_cache_memcached_handle_t *mandle = handle;
218
219 memcached_return_t ret;
220
221 TALLOC_CTX *pool;
222 char *to_store;
223
224 pool = talloc_pool(NULL, 1024);
225 if (!pool) return CACHE_ERROR;
226
227 if (cache_serialize(pool, &to_store, c) < 0) {
228 talloc_free(pool);
229
230 return CACHE_ERROR;
231 }
232
233 ret = memcached_set(mandle->handle, (char const *)c->key.vb_strvalue, c->key.vb_length,
234 to_store ? to_store : "",
235 to_store ? talloc_strlen(to_store) : 0, fr_unix_time_to_sec(c->expires), 0);
236 talloc_free(pool);
237 if (ret != MEMCACHED_SUCCESS) {
238 RERROR("Failed storing entry: %s: %s", memcached_strerror(mandle->handle, ret),
239 memcached_last_error_message(mandle->handle));
240
241 return memcached_fatal(ret) ? CACHE_RECONNECT : CACHE_ERROR;
242 }
243
244 return CACHE_OK;
245}
246
247/** Call delete the cache entry from memcached
248 *
249 * @copydetails cache_entry_expire_t
250 */
252 request_t *request, void *handle, fr_value_box_t const *key)
253{
254 rlm_cache_memcached_handle_t *mandle = handle;
255
256 memcached_return_t ret;
257
258 ret = memcached_delete(mandle->handle, (char const *)key->vb_strvalue, key->vb_length, 0);
259 switch (ret) {
260 case MEMCACHED_SUCCESS:
261 return CACHE_OK;
262
263 case MEMCACHED_DATA_DOES_NOT_EXIST:
264 return CACHE_MISS;
265
266 default:
267 RERROR("Failed deleting entry: %s", memcached_last_error_message(mandle->handle));
268 return memcached_fatal(ret) ? CACHE_RECONNECT : CACHE_ERROR;
269 }
270}
271
272/** Get a memcached handle
273 *
274 * @copydetails cache_acquire_t
275 */
276static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *instance,
277 UNUSED request_t *request)
278{
279 rlm_cache_memcached_t *driver = instance;
280 rlm_cache_handle_t *mandle;
282
283 mandle = memcached_slab_reserve(t->slab);
284 if (!mandle) {
285 *handle = NULL;
286 return -1;
287 }
288 *handle = mandle;
289
290 return 0;
291}
292
293/** Release a memcached handle
294 *
295 * @copydetails cache_release_t
296 */
297static void mod_conn_release(UNUSED rlm_cache_config_t const *config, UNUSED void *instance,
298 UNUSED request_t *request, rlm_cache_handle_t *handle)
299{
300 memcached_slab_release(handle);
301}
302
303/** Reconnect a memcached handle
304 *
305 * @copydetails cache_reconnect_t
306 */
307static int mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *instance,
308 UNUSED request_t *request)
309{
310 rlm_cache_memcached_t *driver = instance;
312 rlm_cache_handle_t *mandle;
313
314 memcached_slab_release(*handle);
315 mandle = memcached_slab_reserve(t->slab);
316 if (!mandle) {
317 *handle = NULL;
318 return -1;
319 }
320 *handle = mandle;
321
322 return 0;
323}
324
326{
327 rlm_cache_memcached_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_cache_memcached_t);
328 rlm_cache_memcached_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_cache_memcached_thread_t);
329
330 t->inst = inst;
331 if (!(t->slab = memcached_slab_list_alloc(t, mctx->el, &inst->reuse, memcached_conn_init, NULL,
332 UNCONST(void *, inst), false, false))) {
333 ERROR("Connection handle pool instantiation failed");
334 return -1;
335 }
336
337 return 0;
338}
339
341{
342 rlm_cache_memcached_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_cache_memcached_thread_t);
343 talloc_free(t->slab);
344 return 0;
345}
346
349 .common = {
350 .magic = MODULE_MAGIC_INIT,
351 .name = "cache_memcached",
354
355 .onload = mod_load,
356 .instantiate = mod_instantiate,
357 .thread_inst_size = sizeof(rlm_cache_memcached_thread_t),
358 .thread_instantiate = mod_thread_instantiate,
359 .thread_detach = mod_thread_detach,
360 },
361
362 .free = cache_entry_free,
363
364 .find = cache_entry_find,
365 .insert = cache_entry_insert,
366 .expire = cache_entry_expire,
367
368 .acquire = mod_conn_get,
369 .release = mod_conn_release,
370 .reconnect = mod_conn_reconnect
371};
static int const char char buffer[256]
Definition acutest.h:576
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define unlikely(_x)
Definition build.h:402
#define UNUSED
Definition build.h:336
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#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:280
#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:309
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
free(array)
talloc_free(hp)
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
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:163
#define RDEBUG2(fmt,...)
#define INFO(fmt,...)
Definition radict.c:63
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.
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:105
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
module_instance_t const * parent
Parent module's instance (if any).
Definition module.h:359
void * data
Thread specific instance data.
Definition module.h:374
static module_thread_instance_t * module_thread(module_instance_t const *mi)
Retrieve module/thread specific instance for a module.
Definition module.h:503
Module instance data.
Definition module.h:287
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
Definition slab.h:124
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition slab.h:75
#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 size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:136
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:4379
static size_t char ** out
Definition value.h:1030