The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: b9fb5b8da9f05817f693eb843b0fbf621e446d5b $
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/value.h>
33 
34 #include "../../rlm_cache.h"
35 #include "../../serialize.h"
36 
37 typedef struct {
38  memcached_st *handle;
40 
41 typedef struct {
42  char const *options; //!< Connection options
45 
46 static const conf_parser_t driver_config[] = {
47  { FR_CONF_OFFSET("options", rlm_cache_memcached_t, options), .dflt = "--SERVER=localhost" },
49 };
50 
51 /** Free a connection handle
52  *
53  * @param mandle to free.
54  */
56 {
57  if (mandle->handle) memcached_free(mandle->handle);
58 
59  return 0;
60 }
61 
62 /** Create a new memcached handle
63  *
64  */
65 static void *mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
66 {
67  rlm_cache_memcached_t *driver = instance;
69 
70  memcached_st *sandle;
71  memcached_return_t ret;
72 
73  sandle = memcached(driver->options, talloc_array_length(driver->options) -1);
74  if (!sandle) {
75  ERROR("Failed creating memcached connection");
76 
77  return NULL;
78  }
79 
80  ret = memcached_behavior_set(sandle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, fr_time_delta_to_msec(timeout));
81  if (ret != MEMCACHED_SUCCESS) {
82  ERROR("%s: %s", memcached_strerror(sandle, ret), memcached_last_error_message(sandle));
83  error:
84  memcached_free(sandle);
85  return NULL;
86  }
87 
88  ret = memcached_version(sandle);
89  if (ret != MEMCACHED_SUCCESS) {
90  ERROR("%s: %s", memcached_strerror(sandle, ret), memcached_last_error_message(sandle));
91  goto error;
92  }
93 
94  mandle = talloc_zero(ctx, rlm_cache_memcached_handle_t);
95  mandle->handle = sandle;
96  talloc_set_destructor(mandle, _mod_conn_free);
97 
98  return mandle;
99 }
100 
101 /** Create a new rlm_cache_memcached instance
102  *
103  * @param[in] mctx Data required for instantiation.
104  * @return
105  * - 0 on success.
106  * - -1 on failure.
107  */
108 static int mod_instantiate(module_inst_ctx_t const *mctx)
109 {
110  rlm_cache_memcached_t *driver = talloc_get_type_abort(mctx->mi->data, rlm_cache_memcached_t);
111  CONF_SECTION *conf = mctx->mi->conf;
112  memcached_return_t ret;
113  char buffer[256];
114  rlm_cache_config_t const *config = talloc_get_type_abort(mctx->mi->parent->data, rlm_cache_config_t);
115 
116  fr_assert(config);
117 
118  snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", mctx->mi->parent->name);
119 
120  ret = libmemcached_check_configuration(driver->options, talloc_array_length(driver->options) -1,
121  buffer, sizeof(buffer));
122  if (ret != MEMCACHED_SUCCESS) {
123  ERROR("%s", buffer);
124  return -1;
125  }
126 
127  driver->pool = module_rlm_connection_pool_init(conf, driver, mod_conn_create, NULL,
128  buffer, "modules.rlm_cache.pool", NULL);
129  if (!driver->pool) return -1;
130 
131  talloc_link_ctx(driver, driver->pool); /* Ensure pool is freed */
132 
133  if (config->max_entries > 0) {
134  ERROR("max_entries is not supported by this driver");
135  return -1;
136  }
137  return 0;
138 }
139 
140 static int mod_load(void)
141 {
142  INFO("%s", memcached_lib_version());
143  return 0;
144 }
145 
146 /** Locate a cache entry in memcached
147  *
148  * @copydetails cache_entry_free_t
149  */
151 {
152  talloc_free(c);
153 }
154 
155 /** Locate a cache entry in memcached
156  *
157  * @copydetails cache_entry_find_t
158  */
160  UNUSED rlm_cache_config_t const *config, UNUSED void *instance,
161  request_t *request, void *handle, fr_value_box_t const *key)
162 {
163  rlm_cache_memcached_handle_t *mandle = handle;
164 
165  memcached_return_t mret;
166  size_t len;
167  int ret;
168  uint32_t flags;
169 
170  char *from_store;
171 
173 
174  from_store = memcached_get(mandle->handle, (char const *)key->vb_strvalue, key->vb_length, &len, &flags, &mret);
175  if (!from_store) {
176  if (mret == MEMCACHED_NOTFOUND) return CACHE_MISS;
177 
178  RERROR("Failed retrieving entry: %s: %s", memcached_strerror(mandle->handle, mret),
179  memcached_last_error_message(mandle->handle));
180 
181  return CACHE_ERROR;
182  }
183  RDEBUG2("Retrieved %zu bytes from memcached", len);
184  RDEBUG2("%s", from_store);
185 
186  MEM(c = talloc_zero(NULL, rlm_cache_entry_t));
187  ret = cache_deserialize(c, request->dict, from_store, len);
188  free(from_store);
189  if (ret < 0) {
190  RPERROR("Invalid entry");
191  error:
192  talloc_free(c);
193  return CACHE_ERROR;
194  }
195  if (unlikely(fr_value_box_copy(c, &c->key, key) < 0)) {
196  RERROR("Failed copying key");
197  goto error;
198  }
199 
200  *out = c;
201 
202  return CACHE_OK;
203 }
204 
205 /** Insert a new entry into the data store
206  *
207  * @copydetails cache_entry_insert_t
208  */
210  request_t *request, void *handle, const rlm_cache_entry_t *c)
211 {
212  rlm_cache_memcached_handle_t *mandle = handle;
213 
214  memcached_return_t ret;
215 
216  TALLOC_CTX *pool;
217  char *to_store;
218 
219  pool = talloc_pool(NULL, 1024);
220  if (!pool) return CACHE_ERROR;
221 
222  if (cache_serialize(pool, &to_store, c) < 0) {
223  talloc_free(pool);
224 
225  return CACHE_ERROR;
226  }
227 
228  ret = memcached_set(mandle->handle, (char const *)c->key.vb_strvalue, c->key.vb_length,
229  to_store ? to_store : "",
230  to_store ? talloc_array_length(to_store) - 1 : 0, fr_unix_time_to_sec(c->expires), 0);
231  talloc_free(pool);
232  if (ret != MEMCACHED_SUCCESS) {
233  RERROR("Failed storing entry: %s: %s", memcached_strerror(mandle->handle, ret),
234  memcached_last_error_message(mandle->handle));
235 
236  return CACHE_ERROR;
237  }
238 
239  return CACHE_OK;
240 }
241 
242 /** Call delete the cache entry from memcached
243  *
244  * @copydetails cache_entry_expire_t
245  */
247  request_t *request, void *handle, fr_value_box_t const *key)
248 {
249  rlm_cache_memcached_handle_t *mandle = handle;
250 
251  memcached_return_t ret;
252 
253  ret = memcached_delete(mandle->handle, (char const *)key->vb_strvalue, key->vb_length, 0);
254  switch (ret) {
255  case MEMCACHED_SUCCESS:
256  return CACHE_OK;
257 
258  case MEMCACHED_DATA_DOES_NOT_EXIST:
259  return CACHE_MISS;
260 
261  default:
262  RERROR("Failed deleting entry: %s", memcached_last_error_message(mandle->handle));
263  return CACHE_ERROR;
264  }
265 }
266 
267 /** Get a memcached handle
268  *
269  * @copydetails cache_acquire_t
270  */
271 static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *instance,
272  request_t *request)
273 {
274  rlm_cache_memcached_t *driver = instance;
275  rlm_cache_handle_t *mandle;
276 
277  *handle = NULL;
278 
279  mandle = fr_pool_connection_get(driver->pool, request);
280  if (!mandle) {
281  *handle = NULL;
282  return -1;
283  }
284  *handle = mandle;
285 
286  return 0;
287 }
288 
289 /** Release a memcached handle
290  *
291  * @copydetails cache_release_t
292  */
293 static void mod_conn_release(UNUSED rlm_cache_config_t const *config, void *instance,
294  request_t *request, rlm_cache_handle_t *handle)
295 {
296  rlm_cache_memcached_t *driver = instance;
297 
298  fr_pool_connection_release(driver->pool, request, handle);
299 }
300 
301 /** Reconnect a memcached handle
302  *
303  * @copydetails cache_reconnect_t
304  */
305 static int mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *instance,
306  request_t *request)
307 {
308  rlm_cache_memcached_t *driver = instance;
309  rlm_cache_handle_t *mandle;
310 
311  mandle = fr_pool_connection_reconnect(driver->pool, request, *handle);
312  if (!mandle) {
313  *handle = NULL;
314  return -1;
315  }
316  *handle = mandle;
317 
318  return 0;
319 }
320 
323  .common = {
324  .magic = MODULE_MAGIC_INIT,
325  .name = "cache_memcached",
326  .inst_size = sizeof(rlm_cache_memcached_t),
328 
329  .onload = mod_load,
331  },
332 
333  .free = cache_entry_free,
334 
335  .find = cache_entry_find,
336  .insert = cache_entry_insert,
337  .expire = cache_entry_expire,
338 
339  .acquire = mod_conn_get,
340  .release = mod_conn_release,
341  .reconnect = mod_conn_reconnect
342 };
static int const char char buffer[256]
Definition: acutest.h:574
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#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
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
A section grouping multiple CONF_PAIR.
Definition: cf_priv.h:101
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
#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
Definition: merged_model.c:33
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
fr_pool_t * module_rlm_connection_pool_init(CONF_SECTION *module, void *opaque, fr_pool_connection_create_t c, fr_pool_connection_alive_t a, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Initialise a module specific connection pool.
Definition: module_rlm.c:308
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition: pool.c:1407
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition: pool.c:1392
void * fr_pool_connection_reconnect(fr_pool_t *pool, request_t *request, void *conn)
Reconnect a suspected inviable connection.
Definition: pool.c:1500
A connection pool.
Definition: pool.c:87
static const conf_parser_t config[]
Definition: base.c:183
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define INFO(fmt,...)
Definition: radict.c:54
static rs_t * conf
Definition: radsniff.c:53
fr_value_box_t key
Key used to identify entry.
Definition: rlm_cache.h:73
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_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 mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *instance, request_t *request)
Reconnect a 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 mod_conn_release(UNUSED rlm_cache_config_t const *config, void *instance, request_t *request, rlm_cache_handle_t *handle)
Release a memcached handle.
static void cache_entry_free(rlm_cache_entry_t *c)
Locate a cache entry in memcached.
rlm_cache_driver_t rlm_cache_memcached
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 const conf_parser_t driver_config[]
char const * options
Connection options.
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
Create a new memcached handle.
static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *instance, request_t *request)
Get a memcached handle.
static int mod_instantiate(module_inst_ctx_t const *mctx)
Create a new rlm_cache_memcached instance.
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
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(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:103
char const * name
Instance name e.g. user_database.
Definition: module.h:335
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
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
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition: talloc.c:171
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