diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c | |
parent | Initial commit. (diff) | |
download | freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.tar.xz freeradius-af754e596a8dbb05ed8580c342e7fe02e08b28e0.zip |
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c')
-rw-r--r-- | src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c b/src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c new file mode 100644 index 0000000..a8161cb --- /dev/null +++ b/src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c @@ -0,0 +1,347 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file rlm_cache_memcached.c + * @brief memcached based cache. + * + * @copyright 2014 The FreeRADIUS server project + */ +#include <libmemcached/memcached.h> + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/rad_assert.h> + +#include "../../rlm_cache.h" +#include "../../serialize.h" + +typedef struct rlm_cache_memcached_handle { + memcached_st *handle; +} rlm_cache_memcached_handle_t; + +typedef struct rlm_cache_memcached { + char const *options; //!< Connection options + fr_connection_pool_t *pool; +} rlm_cache_memcached_t; + +static const CONF_PARSER driver_config[] = { + { "options", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_cache_memcached_t, options), "--SERVER=localhost" }, + CONF_PARSER_TERMINATOR +}; + +/** Free a connection handle + * + * @param mandle to free. + */ +static int _mod_conn_free(rlm_cache_memcached_handle_t *mandle) +{ + if (mandle->handle) memcached_free(mandle->handle); + + return 0; +} + +/** Create a new memcached handle + * + * @param ctx to allocate handle in. + * @param instance data. + */ +static void *mod_conn_create(TALLOC_CTX *ctx, void *instance) +{ + rlm_cache_t *inst = instance; + rlm_cache_memcached_t *driver = inst->driver; + rlm_cache_memcached_handle_t *mandle; + + memcached_st *sandle; + memcached_return_t ret; + + sandle = memcached(driver->options, talloc_array_length(driver->options) -1); + if (!sandle) { + ERROR("rlm_cache_memcached: Failed creating memcached connection"); + + return NULL; + } + + ret = memcached_version(sandle); + if (ret != MEMCACHED_SUCCESS) { + ERROR("rlm_cache_memcached: Failed getting server info: %s: %s", memcached_strerror(sandle, ret), + memcached_last_error_message(sandle)); + memcached_free(sandle); + return NULL; + } + + mandle = talloc_zero(ctx, rlm_cache_memcached_handle_t); + mandle->handle = sandle; + talloc_set_destructor(mandle, _mod_conn_free); + + return mandle; +} + +/** Cleanup a rlm_cache_memcached instance + * + * @param driver to free. + * @return 0 + */ +static int _mod_detach(rlm_cache_memcached_t *driver) +{ + fr_connection_pool_free(driver->pool); + return 0; +} + +/** Create a new rlm_cache_memcached instance + * + * @param conf memcached specific conf section. + * @param inst main rlm_cache instance. + * @return 0 on success, -1 on failure. + */ +static int mod_instantiate(CONF_SECTION *conf, rlm_cache_t *inst) +{ + rlm_cache_memcached_t *driver; + memcached_return_t ret; + + char buffer[256]; + + static bool version_done; + + buffer[0] = '\0'; + + /* + * Get version info from the libmemcached API. + */ + if (!version_done) { + version_done = true; + + INFO("rlm_cache_memcached: libmemcached version: %s", memcached_lib_version()); + } + + driver = talloc_zero(inst, rlm_cache_memcached_t); + talloc_set_destructor(driver, _mod_detach); + + if (cf_section_parse(conf, driver, driver_config) < 0) return -1; + + ret = libmemcached_check_configuration(driver->options, talloc_array_length(driver->options) -1, + buffer, sizeof(buffer)); + if (ret != MEMCACHED_SUCCESS) { + ERROR("rlm_cache_memcached: Failed validating options string: %s", buffer); + return -1; + } + + inst->driver = driver; + + snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", inst->name); + + driver->pool = fr_connection_pool_module_init(conf, inst, mod_conn_create, NULL, buffer); + if (!driver->pool) return -1; + + if (inst->max_entries > 0) WARN("rlm_cache_memcached: max_entries is not supported by this driver"); + + return 0; +} + +static void cache_entry_free(rlm_cache_entry_t *c) +{ + talloc_free(c); +} + +/** Locate a cache entry in memcached + * + * @param out Where to write the pointer to the cach entry. + * @param inst main rlm_cache instance. + * @param request The current request. + * @param handle Pointer to memcached handle. + * @param key to search for. + * @return CACHE_OK on success CACHE_MISS if no entry found, CACHE_ERROR on error. + */ +static cache_status_t cache_entry_find(rlm_cache_entry_t **out, UNUSED rlm_cache_t *inst, REQUEST *request, + rlm_cache_handle_t **handle, char const *key) +{ + rlm_cache_memcached_handle_t *mandle = *handle; + + memcached_return_t mret; + size_t len; + int ret; + uint32_t flags; + + char *from_store; + + rlm_cache_entry_t *c; + + from_store = memcached_get(mandle->handle, key, strlen(key), &len, &flags, &mret); + if (!from_store) { + if (mret == MEMCACHED_NOTFOUND) return CACHE_MISS; + + RERROR("Failed retrieving entry for key \"%s\": %s: %s", key, memcached_strerror(mandle->handle, mret), + memcached_last_error_message(mandle->handle)); + + return CACHE_ERROR; + } + RDEBUG2("Retrieved %zu bytes from memcached", len); + RDEBUG2("%s", from_store); + + c = talloc_zero(NULL, rlm_cache_entry_t); + ret = cache_deserialize(c, from_store, len); + free(from_store); + if (ret < 0) { + RERROR("%s", fr_strerror()); + talloc_free(c); + return CACHE_ERROR; + } + c->key = talloc_strdup(c, key); + *out = c; + + return CACHE_OK; +} + +/** Insert a new entry into the data store + * + * @param inst main rlm_cache instance. + * @param request The current request. + * @param handle Pointer to memcached handle. + * @param c entry to insert. + * @return CACHE_OK on success else CACHE_ERROR on error. + */ +static cache_status_t cache_entry_insert(UNUSED rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, + rlm_cache_entry_t *c) +{ + rlm_cache_memcached_handle_t *mandle = *handle; + + memcached_return_t ret; + + TALLOC_CTX *pool; + char *to_store; + + pool = talloc_pool(NULL, 1024); + if (!pool) return CACHE_ERROR; + + if (cache_serialize(pool, &to_store, c) < 0) { + talloc_free(pool); + + return CACHE_ERROR; + } + + ret = memcached_set(mandle->handle, c->key, talloc_array_length(c->key) - 1, + to_store ? to_store : "", + to_store ? talloc_array_length(to_store) - 1 : 0, c->expires, 0); + talloc_free(pool); + if (ret != MEMCACHED_SUCCESS) { + RERROR("Failed storing entry with key \"%s\": %s: %s", c->key, + memcached_strerror(mandle->handle, ret), + memcached_last_error_message(mandle->handle)); + + return CACHE_ERROR; + } + + return CACHE_OK; +} + +/** Call delete the cache entry from memcached + * + * @param inst main rlm_cache instance. + * @param request The current request. + * @param handle Pointer to memcached handle. + * @param c entry to expire. + * @return CACHE_OK on success else CACHE_ERROR. + */ +static cache_status_t cache_entry_expire(UNUSED rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, + rlm_cache_entry_t *c) +{ + rlm_cache_memcached_handle_t *mandle = *handle; + + memcached_return_t ret; + + ret = memcached_delete(mandle->handle, c->key, talloc_array_length(c->key) - 1, 0); + if (ret != MEMCACHED_SUCCESS) { + RERROR("Failed deleting entry with key \"%s\": %s", c->key, + memcached_last_error_message(mandle->handle)); + + return CACHE_ERROR; + } + + return CACHE_OK; +} + +/** Get a memcached handle + * + * @param out Where to write the handle. + * @param inst rlm_cache instance. + * @param request The current request. + */ +static int mod_conn_get(rlm_cache_handle_t **out, rlm_cache_t *inst, UNUSED REQUEST *request) +{ + rlm_cache_memcached_t *driver = inst->driver; + rlm_cache_handle_t *mandle; + + *out = NULL; + + mandle = fr_connection_get(driver->pool); + if (!mandle) { + *out = NULL; + return -1; + } + *out = mandle; + + return 0; +} + +/** Release a socket + * + * @param inst main rlm_cache instance. + * @param request The current request. + * @param handle Pointer to the handle to release (will be set to NULL). + */ +static void mod_conn_release(rlm_cache_t *inst, UNUSED REQUEST *request, rlm_cache_handle_t **handle) +{ + rlm_cache_memcached_t *driver = inst->driver; + + fr_connection_release(driver->pool, *handle); + *handle = NULL; +} + +/** Reconnect a socket + * + * @param inst main rlm_cache instance. + * @param request The current request. + * @param handle Pointer to the handle to reconnect (will be set to NULL if reconnection fails). + */ +static int mod_conn_reconnect(rlm_cache_t *inst, UNUSED REQUEST *request, rlm_cache_handle_t **handle) +{ + rlm_cache_memcached_t *driver = inst->driver; + rlm_cache_handle_t *mandle; + + mandle = fr_connection_reconnect(driver->pool, *handle); + if (!mandle) { + *handle = NULL; + return -1; + } + *handle = mandle; + + return 0; +} + +extern cache_module_t rlm_cache_memcached; +cache_module_t rlm_cache_memcached = { + .name = "rlm_cache_memcached", + .instantiate = mod_instantiate, + .free = cache_entry_free, + + .find = cache_entry_find, + .insert = cache_entry_insert, + .expire = cache_entry_expire, + + .acquire = mod_conn_get, + .release = mod_conn_release, + .reconnect = mod_conn_reconnect +}; |