All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: c93bb687087dae23bde27bdcafeb68e0aa1bf200 $
19  * @file rlm_cache_memcached.c
20  * @brief memcached based cache.
21  *
22  * @copyright 2014 The FreeRADIUS server project
23  */
24 #include <libmemcached/memcached.h>
25 
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/rad_assert.h>
29 
30 #include "../../rlm_cache.h"
31 #include "../../serialize.h"
32 
34  memcached_st *handle;
36 
37 typedef struct rlm_cache_memcached {
38  char const *options; //!< Connection options
41 
42 static const CONF_PARSER driver_config[] = {
43  { FR_CONF_OFFSET("options", PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_cache_memcached_t, options), .dflt = "--SERVER=localhost" },
45 };
46 
47 /** Free a connection handle
48  *
49  * @param mandle to free.
50  */
52 {
53  if (mandle->handle) memcached_free(mandle->handle);
54 
55  return 0;
56 }
57 
58 /** Create a new memcached handle
59  *
60  */
61 static void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
62 {
63  rlm_cache_memcached_t *driver = instance;
65 
66  memcached_st *sandle;
67  memcached_return_t ret;
68 
69  sandle = memcached(driver->options, talloc_array_length(driver->options) -1);
70  if (!sandle) {
71  ERROR("rlm_cache_memcached: Failed creating memcached connection");
72 
73  return NULL;
74  }
75 
76  ret = memcached_behavior_set(sandle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, (uint64_t)FR_TIMEVAL_TO_MS(timeout));
77  if (ret != MEMCACHED_SUCCESS) {
78  ERROR("rlm_cache_memcached: Failed setting connection timeout: %s: %s", memcached_strerror(sandle, ret),
79  memcached_last_error_message(sandle));
80  error:
81  memcached_free(sandle);
82  return NULL;
83  }
84 
85  ret = memcached_version(sandle);
86  if (ret != MEMCACHED_SUCCESS) {
87  ERROR("rlm_cache_memcached: Failed getting server info: %s: %s", memcached_strerror(sandle, ret),
88  memcached_last_error_message(sandle));
89  goto error;
90  }
91 
92  mandle = talloc_zero(ctx, rlm_cache_memcached_handle_t);
93  mandle->handle = sandle;
94  talloc_set_destructor(mandle, _mod_conn_free);
95 
96  return mandle;
97 }
98 
99 /** Create a new rlm_cache_memcached instance
100  *
101  * @copydetails cache_instantiate_t
102  */
103 static int mod_instantiate(CONF_SECTION *conf, rlm_cache_config_t const *config, void *driver_inst)
104 {
105  static bool version_done;
106 
107  rlm_cache_memcached_t *driver = driver_inst;
108  memcached_return_t ret;
109 
110  char buffer[256];
111 
112 
113  buffer[0] = '\0';
114 
115  /*
116  * Get version info from the libmemcached API.
117  */
118  if (!version_done) {
119  version_done = true;
120 
121  INFO("rlm_cache_memcached: libmemcached version: %s", memcached_lib_version());
122  }
123 
124  if (cf_section_parse(conf, driver, driver_config) < 0) return -1;
125 
126  ret = libmemcached_check_configuration(driver->options, talloc_array_length(driver->options) -1,
127  buffer, sizeof(buffer));
128  if (ret != MEMCACHED_SUCCESS) {
129  ERROR("rlm_cache_memcached: Failed validating options string: %s", buffer);
130  return -1;
131  }
132 
133  snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", config->name);
134 
135  driver->pool = module_connection_pool_init(conf, driver, mod_conn_create, NULL, buffer);
136  if (!driver->pool) return -1;
137 
138  fr_talloc_link_ctx(driver, driver->pool); /* Ensure pool is freed */
139 
140  if (config->max_entries > 0) {
141  ERROR("rlm_cache_memcached: max_entries is not supported by this driver");
142  return -1;
143  }
144  return 0;
145 }
146 
147 /** Locate a cache entry in memcached
148  *
149  * @copydetails cache_entry_free_t
150  */
152 {
153  talloc_free(c);
154 }
155 
156 /** Locate a cache entry in memcached
157  *
158  * @copydetails cache_entry_find_t
159  */
161  UNUSED rlm_cache_config_t const *config, UNUSED void *driver_inst,
162  REQUEST *request, void *handle, uint8_t const *key, size_t key_len)
163 {
164  rlm_cache_memcached_handle_t *mandle = handle;
165 
166  memcached_return_t mret;
167  size_t len;
168  int ret;
169  uint32_t flags;
170 
171  char *from_store;
172 
174 
175  from_store = memcached_get(mandle->handle, (char const *)key, key_len, &len, &flags, &mret);
176  if (!from_store) {
177  if (mret == MEMCACHED_NOTFOUND) return CACHE_MISS;
178 
179  RERROR("Failed retrieving entry: %s: %s", memcached_strerror(mandle->handle, mret),
180  memcached_last_error_message(mandle->handle));
181 
182  return CACHE_ERROR;
183  }
184  RDEBUG2("Retrieved %zu bytes from memcached", len);
185  RDEBUG2("%s", from_store);
186 
187  c = talloc_zero(NULL, rlm_cache_entry_t);
188  ret = cache_deserialize(c, from_store, len);
189  free(from_store);
190  if (ret < 0) {
191  RERROR("%s", fr_strerror());
192  talloc_free(c);
193  return CACHE_ERROR;
194  }
195  c->key = talloc_memdup(c, key, key_len);
196  *out = c;
197 
198  return CACHE_OK;
199 }
200 
201 /** Insert a new entry into the data store
202  *
203  * @copydetails cache_entry_insert_t
204  */
205 static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, UNUSED void *driver_inst,
206  REQUEST *request, void *handle, const rlm_cache_entry_t *c)
207 {
208  rlm_cache_memcached_handle_t *mandle = handle;
209 
210  memcached_return_t ret;
211 
212  TALLOC_CTX *pool;
213  char *to_store;
214 
215  pool = talloc_pool(NULL, 1024);
216  if (!pool) return CACHE_ERROR;
217 
218  if (cache_serialize(pool, &to_store, c) < 0) {
219  talloc_free(pool);
220 
221  return CACHE_ERROR;
222  }
223 
224  ret = memcached_set(mandle->handle, (char const *)c->key, c->key_len,
225  to_store ? to_store : "",
226  to_store ? talloc_array_length(to_store) - 1 : 0, c->expires, 0);
227  talloc_free(pool);
228  if (ret != MEMCACHED_SUCCESS) {
229  RERROR("Failed storing entry: %s: %s", memcached_strerror(mandle->handle, ret),
230  memcached_last_error_message(mandle->handle));
231 
232  return CACHE_ERROR;
233  }
234 
235  return CACHE_OK;
236 }
237 
238 /** Call delete the cache entry from memcached
239  *
240  * @copydetails cache_entry_expire_t
241  */
242 static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, UNUSED void *driver_inst,
243  REQUEST *request, void *handle, uint8_t const *key, size_t key_len)
244 {
245  rlm_cache_memcached_handle_t *mandle = handle;
246 
247  memcached_return_t ret;
248 
249  ret = memcached_delete(mandle->handle, (char const *)key, key_len, 0);
250  switch (ret) {
251  case MEMCACHED_SUCCESS:
252  return CACHE_OK;
253 
254  case MEMCACHED_DATA_DOES_NOT_EXIST:
255  return CACHE_MISS;
256 
257  default:
258  RERROR("Failed deleting entry: %s", memcached_last_error_message(mandle->handle));
259  return CACHE_ERROR;
260  }
261 }
262 
263 /** Get a memcached handle
264  *
265  * @copydetails cache_acquire_t
266  */
267 static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *driver_inst,
268  UNUSED REQUEST *request)
269 {
270  rlm_cache_memcached_t *driver = driver_inst;
271  rlm_cache_handle_t *mandle;
272 
273  *handle = NULL;
274 
275  mandle = fr_connection_get(driver->pool);
276  if (!mandle) {
277  *handle = NULL;
278  return -1;
279  }
280  *handle = mandle;
281 
282  return 0;
283 }
284 
285 /** Release a memcached handle
286  *
287  * @copydetails cache_release_t
288  */
289 static void mod_conn_release(UNUSED rlm_cache_config_t const *config, void *driver_inst,
290  UNUSED REQUEST *request, rlm_cache_handle_t *handle)
291 {
292  rlm_cache_memcached_t *driver = driver_inst;
293 
294  fr_connection_release(driver->pool, handle);
295 }
296 
297 /** Reconnect a memcached handle
298  *
299  * @copydetails cache_reconnect_t
300  */
301 static int mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *driver_inst,
302  UNUSED REQUEST *request)
303 {
304  rlm_cache_memcached_t *driver = driver_inst;
305  rlm_cache_handle_t *mandle;
306 
307  mandle = fr_connection_reconnect(driver->pool, *handle);
308  if (!mandle) {
309  *handle = NULL;
310  return -1;
311  }
312  *handle = mandle;
313 
314  return 0;
315 }
316 
318 cache_driver_t rlm_cache_memcached = {
319  .name = "rlm_cache_memcached",
320  .instantiate = mod_instantiate,
321  .inst_size = sizeof(rlm_cache_memcached_t),
322  .free = cache_entry_free,
323 
324  .find = cache_entry_find,
325  .insert = cache_entry_insert,
326  .expire = cache_entry_expire,
327 
328  .acquire = mod_conn_get,
329  .release = mod_conn_release,
330  .reconnect = mod_conn_reconnect
331 };
static void cache_entry_free(rlm_cache_entry_t *c)
Locate a cache entry in memcached.
Definition: rlm_cache.h:76
#define RERROR(fmt,...)
Definition: log.h:207
Fatal error.
Definition: rlm_cache.h:37
Cache entry notfound.
Definition: rlm_cache.h:39
static const CONF_PARSER driver_config[]
#define INFO(fmt,...)
Definition: log.h:143
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
#define UNUSED
Definition: libradius.h:134
static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, UNUSED void *driver_inst, REQUEST *request, void *handle, uint8_t const *key, size_t key_len)
Call delete the cache entry from memcached.
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
static cache_status_t cache_entry_find(rlm_cache_entry_t **out, UNUSED rlm_cache_config_t const *config, UNUSED void *driver_inst, REQUEST *request, void *handle, uint8_t const *key, size_t key_len)
Locate a cache entry in memcached.
uint32_t max_entries
Maximum entries allowed.
Definition: rlm_cache.h:52
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
struct rlm_cache_memcached rlm_cache_memcached_t
static float timeout
Definition: radclient.c:43
cache_status_t
Definition: rlm_cache.h:35
static int mod_instantiate(CONF_SECTION *conf, rlm_cache_config_t const *config, void *driver_inst)
Create a new rlm_cache_memcached instance.
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
size_t key_len
Length of key data.
Definition: rlm_cache.h:78
struct rlm_cache_memcached_handle rlm_cache_memcached_handle_t
char const * options
Connection options.
fr_connection_pool_t * module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_connection_create_t c, fr_connection_alive_t a, char const *prefix)
Initialise a module specific connection pool.
Definition: modules.c:1759
time_t expires
When the entry expires.
Definition: rlm_cache.h:81
static void * mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Create a new memcached handle.
static int mod_conn_reconnect(void **handle, UNUSED rlm_cache_config_t const *config, void *driver_inst, UNUSED REQUEST *request)
Reconnect a memcached handle.
static int _mod_conn_free(rlm_cache_memcached_handle_t *mandle)
Free a connection handle.
int cf_section_parse(CONF_SECTION *, void *base, CONF_PARSER const *variables)
Parse a configuration section into user-supplied variables.
Definition: conffile.c:2234
void rlm_cache_handle_t
Definition: rlm_cache.h:31
static rs_t * conf
Definition: radsniff.c:46
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
static int mod_conn_get(void **handle, UNUSED rlm_cache_config_t const *config, void *driver_inst, UNUSED REQUEST *request)
Get a memcached handle.
#define RDEBUG2(fmt,...)
Definition: log.h:244
Cache entry found/updated.
Definition: rlm_cache.h:38
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
char const * name
Name of xlat function to register.
Definition: rlm_cache.h:48
static void mod_conn_release(UNUSED rlm_cache_config_t const *config, void *driver_inst, UNUSED REQUEST *request, rlm_cache_handle_t *handle)
Release a memcached handle.
fr_connection_pool_t * pool
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
Definition: connection.c:1291
A connection pool.
Definition: connection.c:85
int fr_talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link a parent and a child context, so the child is freed before the parent.
Definition: misc.c:105
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: conffile.h:200
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
Definition: connection.c:1305
#define FR_TIMEVAL_TO_MS(_x)
Definition: conffile.h:235
String of printable characters.
Definition: radius.h:33
char const * name
Driver name.
Definition: rlm_cache.h:279
static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, UNUSED void *driver_inst, REQUEST *request, void *handle, const rlm_cache_entry_t *c)
Insert a new entry into the data store.
uint8_t const * key
Key used to identify entry.
Definition: rlm_cache.h:77
void * fr_connection_reconnect(fr_connection_pool_t *pool, void *conn)
Reconnect a suspected inviable connection.
Definition: connection.c:1367
#define ERROR(fmt,...)
Definition: log.h:145
int cache_deserialize(rlm_cache_entry_t *c, char *in, ssize_t inlen)
Converts a serialized cache entry back into a structure.
Definition: serialize.c:103
cache_driver_t rlm_cache_memcached
Configuration for the rlm_cache module.
Definition: rlm_cache.h:47