From: Alan T. DeKok Date: Sun, 13 Nov 2011 12:02:21 +0000 (+0100) Subject: Clean up for 3.0 X-Git-Tag: release_3_0_0_beta0~505 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cafe2ea0e4520cfdba591752c1dea4d725143387;p=thirdparty%2Ffreeradius-server.git Clean up for 3.0 Use new connection pool API. Document it. Use a more consistent module configuration Less code, and it does more --- diff --git a/raddb/modules/redis b/raddb/modules/redis index d7605d98886..600aaa2c93d 100644 --- a/raddb/modules/redis +++ b/raddb/modules/redis @@ -19,17 +19,36 @@ redis { # We recommend using a strong password. # password = thisisreallysecretandhardtoguess - # The number of connections to open to the database. - num_connections = 20 + # + # Information for the connection pool. The configuration items + # below are the same for all modules which use the new + # connection pool. + # + pool { + # start this many connections + start = 1 - # If a connection fails, retry after this time. - connect_failure_retry_delay = 60 + # Keep at least "min" connections open + min = 1 - # Set the maximum lifetime for one connection. - # Use 0 for "lives forever" - lifetime = 86400 + # No more than "max" connections at any one time + max = 10 - # Set the maximum queries used for one connection. - # Use 0 for "no limit" - max_queries = 0 -} \ No newline at end of file + # try to keep "spare" connections + spare = 0 + + # If we have spare connections for "cleanup_delay" seconds, + # start deleting them + cleanup_delay = 300 + + # connections last no more than "lifeime" seconds. + lifetime = 86400 + + # close idle connections are "idle_timeout" seconds + idle_timeout = 600 + + # allow no more than "uses" queries through a connection. + # after that, close it and open a new one. + uses = 0 + } +} diff --git a/raddb/modules/rediswho b/raddb/modules/rediswho index 470dbadcad3..8963af049e3 100644 --- a/raddb/modules/rediswho +++ b/raddb/modules/rediswho @@ -5,6 +5,8 @@ # # Configuration file for the "rediswho" module. # +# This module tracks the last set of login sessions for a user. +# rediswho { # How many sessions to keep track of per user. # If there are more than this number, older sessions are deleted. @@ -14,15 +16,31 @@ rediswho { # an update in this time will be automatically expired. expire-time = 86400 - start-insert = "LPUSH %{User-Name} %l,%{Acct-Session-Id},%{NAS-IP-Address},%{Acct-Session-Time},%{Framed-IP-Address},%{Acct-Input-Gigawords:-0},%{Acct-Output-Gigawords:-0},%{Acct-Input-Octets:-0},%{Acct-Output-Octets:-0}" - start-trim = "LTRIM %{User-Name} 0 ${trim-count}" - start-expire = "EXPIRE %{User-Name} ${expire-time}" + # + # Each subsection contains insert / trim / expire queries. + # The subsections are named after the contents of the + # Acct-Status-Type attribute. See dictionary.rfc2866 for names + # of the various Acct-Status-Type values, or look at the output + # of debug mode. + # + # This module supports *any* Acct-Status-Type. Just add a subsection + # of the appropriate name, along with insert / trim / expire queries. + # + Start { + insert = "LPUSH %{User-Name} %l,%{Acct-Session-Id},%{NAS-IP-Address},%{Acct-Session-Time},%{Framed-IP-Address},%{Acct-Input-Gigawords:-0},%{Acct-Output-Gigawords:-0},%{Acct-Input-Octets:-0},%{Acct-Output-Octets:-0}" + trim = "LTRIM %{User-Name} 0 ${trim-count}" + expire = "EXPIRE %{User-Name} ${expire-time}" + } - alive-insert = "LPUSH %{User-Name} %l,%{Acct-Session-Id},%{NAS-IP-Address},%{Acct-Session-Time},%{Framed-IP-Address},%{Acct-Input-Gigawords:-0},%{Acct-Output-Gigawords:-0},%{Acct-Input-Octets:-0},%{Acct-Output-Octets:-0}" - alive-trim = "LTRIM %{User-Name} 0 ${trim-count}" - alive-expire = "EXPIRE %{User-Name} ${expire-time}" + Interim-Update { + insert = "LPUSH %{User-Name} %l,%{Acct-Session-Id},%{NAS-IP-Address},%{Acct-Session-Time},%{Framed-IP-Address},%{Acct-Input-Gigawords:-0},%{Acct-Output-Gigawords:-0},%{Acct-Input-Octets:-0},%{Acct-Output-Octets:-0}" + trim = "LTRIM %{User-Name} 0 ${trim-count}" + expire = "EXPIRE %{User-Name} ${expire-time}" + } - stop-insert = "LPUSH %{User-Name} %l,%{Acct-Session-Id},%{NAS-IP-Address},%{Acct-Session-Time},%{Framed-IP-Address},%{Acct-Input-Gigawords:-0},%{Acct-Output-Gigawords:-0},%{Acct-Input-Octets:-0},%{Acct-Output-Octets:-0}" - stop-trim = "LTRIM %{User-Name} 0 ${trim-count}" - stop-expire = "EXPIRE %{User-Name} ${expire-time}" + Stop { + insert = "LPUSH %{User-Name} %l,%{Acct-Session-Id},%{NAS-IP-Address},%{Acct-Session-Time},%{Framed-IP-Address},%{Acct-Input-Gigawords:-0},%{Acct-Output-Gigawords:-0},%{Acct-Input-Octets:-0},%{Acct-Output-Octets:-0}" + trim = "LTRIM %{User-Name} 0 ${trim-count}" + expire = "EXPIRE %{User-Name} ${expire-time}" + } } diff --git a/src/modules/rlm_redis/rlm_redis.c b/src/modules/rlm_redis/rlm_redis.c index 515533e1c25..144f319faea 100644 --- a/src/modules/rlm_redis/rlm_redis.c +++ b/src/modules/rlm_redis/rlm_redis.c @@ -31,111 +31,79 @@ RCSID("$Id$") #include "rlm_redis.h" static const CONF_PARSER module_config[] = { - { "num_connections", PW_TYPE_INTEGER, - offsetof(REDIS_INST, numconnections), NULL, "20"}, { "hostname", PW_TYPE_STRING_PTR, offsetof(REDIS_INST, hostname), NULL, "127.0.0.1"}, { "port", PW_TYPE_INTEGER, offsetof(REDIS_INST, port), NULL, "6379"}, { "password", PW_TYPE_STRING_PTR, offsetof(REDIS_INST, password), NULL, NULL}, - {"connect_failure_retry_delay", PW_TYPE_INTEGER, - offsetof(REDIS_INST, connect_failure_retry_delay), NULL, "60"}, - {"lifetime", PW_TYPE_INTEGER, - offsetof(REDIS_INST, lifetime), NULL, "0"}, - {"max_queries", PW_TYPE_INTEGER, - offsetof(REDIS_INST, max_queries), NULL, "0"}, { NULL, -1, 0, NULL, NULL} /* end the list */ }; -static int redis_close_socket(REDIS_INST *inst, REDISSOCK *dissocket) +static int redis_delete_conn(UNUSED void *ctx, void *conn) { - radlog(L_INFO, "rlm_redis (%s): Closing socket %d", - inst->xlat_name, dissocket->id); + REDISSOCK *dissocket = conn; - if (dissocket->state == sockconnected) { - redisFree(dissocket->conn); - } + redisFree(dissocket->conn); -#ifdef HAVE_PTHREAD_H - pthread_mutex_destroy(&dissocket->mutex); -#endif + if (dissocket->reply) { + freeReplyObject(dissocket->reply); + dissocket->reply = NULL; + } free(dissocket); return 1; } -static int connect_single_socket(REDIS_INST *inst, REDISSOCK *dissocket) +static void *redis_create_conn(void *ctx) { - radlog(L_INFO, "rlm_redis (%s): Attempting to connect #%d", - inst->xlat_name, dissocket->id); - - dissocket->conn = redisConnect(inst->hostname, inst->port); + REDIS_INST *inst = ctx; + REDISSOCK *dissocket = NULL; + redisContext *conn; - if (!dissocket->conn->err) { - radlog(L_INFO, "rlm_redis (%s): Connected new DB handle, #%d", - inst->xlat_name, dissocket->id); - - dissocket->state = sockconnected; - - dissocket->queries = 0; - return 0; - } + conn = redisConnect(inst->hostname, inst->port); + if (!dissocket->conn->err) goto do_alloc; if (inst->password) { + redisReply *reply = NULL; char buffer[1024]; snprintf(buffer, sizeof(buffer), "AUTH %s", inst->password); - dissocket->reply = redisCommand(dissocket->conn, buffer); - if (!dissocket->reply) { + reply = redisCommand(dissocket->conn, buffer); + if (!reply) { radlog(L_ERR, "rlm_redis (%s): Failed to run AUTH", inst->xlat_name); - redis_close_socket(inst, dissocket); - return -1; + do_close: + if (reply) freeReplyObject(reply); + redisFree(conn); + return NULL; } - switch (dissocket->reply->type) { + switch (reply->type) { case REDIS_REPLY_STATUS: - if (strcmp(dissocket->reply->str, "OK") != 0) { + if (strcmp(reply->str, "OK") != 0) { radlog(L_ERR, "rlm_redis (%s): Failed authentication: reply %s", inst->xlat_name, dissocket->reply->str); - redis_close_socket(inst, dissocket); - return -1; + goto do_close; } break; /* else it's OK */ default: radlog(L_ERR, "rlm_redis (%s): Unexpected reply to AUTH", inst->xlat_name); - redis_close_socket(inst, dissocket); - return -1; + goto do_close; } } - /* - * Error, or redis is DOWN. - */ - radlog(L_CONS | L_ERR, "rlm_redis (%s): Failed to connect DB handle #%d", - inst->xlat_name, dissocket->id); - inst->connect_after = time(NULL) + inst->connect_failure_retry_delay; - dissocket->state = sockunconnected; - return -1; -} - -static void redis_poolfree(REDIS_INST * inst) -{ - REDISSOCK *cur; - REDISSOCK *next; +do_alloc: + dissocket = rad_malloc(sizeof(*dissocket)); + memset(dissocket, 0, sizeof(*dissocket)); + dissocket->conn = conn; - for (cur = inst->redispool; cur; cur = next) { - next = cur->next; - redis_close_socket(inst, cur); - } - - inst->redispool = NULL; + return dissocket; } static size_t redis_escape_func(char *out, size_t outlen, const char *in) @@ -204,7 +172,8 @@ static int redis_xlat(void *instance, REQUEST *request, return 0; } - if ((dissocket = redis_get_socket(inst)) == NULL) { + dissocket = fr_connection_get(inst->pool); + if (!dissocket) { radlog(L_ERR, "rlm_redis (%s): redis_get_socket() failed", inst->xlat_name); @@ -212,11 +181,8 @@ static int redis_xlat(void *instance, REQUEST *request, } /* Query failed for some reason, release socket and return */ - if (rlm_redis_query(dissocket, inst, querystr) < 0) { - rlm_redis_finish_query(dissocket); - redis_release_socket(inst,dissocket); - - return 0; + if (rlm_redis_query(&dissocket, inst, querystr) < 0) { + goto release; } switch (dissocket->reply->type) { @@ -242,17 +208,15 @@ static int redis_xlat(void *instance, REQUEST *request, if ((ret >= freespace) || (buffer_ptr == NULL)) { RDEBUG("rlm_redis (%s): Can't write result, insufficient space or unsupported result\n", inst->xlat_name); - - rlm_redis_finish_query(dissocket); - redis_release_socket(inst,dissocket); - - return 0; + ret = 0; + goto release; } - strlcpy(out,buffer_ptr,freespace); - + strlcpy(out, buffer_ptr, freespace); + +release: rlm_redis_finish_query(dissocket); - redis_release_socket(inst,dissocket); + fr_connection_release(inst->pool, dissocket); return ret; } @@ -265,7 +229,7 @@ static int redis_detach(void *instance) { REDIS_INST *inst = instance; - redis_poolfree(inst); + fr_connection_pool_delete(inst->pool); if (inst->xlat_name) { xlat_unregister(inst->xlat_name, (RAD_XLAT_FUNC)redis_xlat); @@ -277,102 +241,42 @@ static int redis_detach(void *instance) return 0; } -static int redis_init_socketpool(REDIS_INST *inst) -{ - int i, rcode; - int success = 0; - REDISSOCK *dissocket; - - inst->connect_after = 0; - inst->redispool = NULL; - - for (i = 0; i < inst->numconnections; i++) { - radlog(L_DBG, "rlm_redis (%s): starting %d", - inst->xlat_name, i); - - dissocket = rad_malloc(sizeof (*dissocket)); - if (dissocket == NULL) { - return -1; - } - memset(dissocket, 0, sizeof (*dissocket)); - dissocket->conn = NULL; - dissocket->id = i; - dissocket->state = sockunconnected; - -#ifdef HAVE_PTHREAD_H - rcode = pthread_mutex_init(&dissocket->mutex, NULL); - if (rcode != 0) { - free(dissocket); - radlog(L_ERR, "rlm_redis: Failed to init lock: %s", - strerror(errno)); - return 0; - } -#endif - - if (time(NULL) > inst->connect_after) { - /* - * This sets the dissocket->state, and - * possibly also inst->connect_after - */ - if (connect_single_socket(inst, dissocket) == 0) { - success = 1; - } - } - - /* Add "dis" socket to the list of sockets - * pun intended - */ - dissocket->next = inst->redispool; - inst->redispool = dissocket; - } - inst->last_used = NULL; - - if (!success) { - radlog(L_DBG, "rlm_redis (%s): Failed to connect to any redis server.", - inst->xlat_name); - } - - return 1; -} - /* - * Free the redis database + * Query the redis database */ -int rlm_redis_query(REDISSOCK *dissocket, REDIS_INST *inst, char *query) +int rlm_redis_query(REDISSOCK **dissocket_p, REDIS_INST *inst, char *query) { - if (!query || !*query) { + REDISSOCK *dissocket; + + if (!query || !*query || !inst || !dissocket_p) { return -1; } + dissocket = *dissocket_p; + DEBUG2("executing query %s", query); dissocket->reply = redisCommand(dissocket->conn, query); - if (dissocket->reply == NULL) { + if (!dissocket->reply) { radlog(L_ERR, "rlm_redis: (%s) REDIS error: %s", inst->xlat_name, dissocket->conn->errstr); - /* close the socket that failed */ - if (dissocket->state == sockconnected) { - redisFree(dissocket->conn); - dissocket->state = sockunconnected; - } - - /* reconnect the socket */ - if (connect_single_socket(inst, dissocket) < 0) { - radlog(L_ERR, "rlm_redis (%s): reconnect failed, database down?", - inst->xlat_name); + dissocket = fr_connection_reconnect(inst->pool, dissocket); + if (!dissocket) { + error: + *dissocket_p = NULL; return -1; } - DEBUG2("executing query %s", query); - /* retry the query on the newly connected socket */ dissocket->reply = redisCommand(dissocket->conn, query); - - if (dissocket->reply == NULL) { + if (!dissocket->reply) { radlog(L_ERR, "rlm_redis (%s): failed after re-connect", inst->xlat_name); - return -1; + fr_connection_del(inst->pool, dissocket); + goto error; } + + *dissocket_p = dissocket; } if (dissocket->reply->type == REDIS_REPLY_ERROR) { @@ -389,189 +293,16 @@ int rlm_redis_query(REDISSOCK *dissocket, REDIS_INST *inst, char *query) } /* - * Clear the redis reply object if any + * Clear the redis reply object if any */ int rlm_redis_finish_query(REDISSOCK *dissocket) { - if (dissocket == NULL) { + if (!dissocket || !dissocket->reply) { return -1; } - if (dissocket->reply != NULL) { - freeReplyObject(dissocket->reply); - } else { - return -1; - } - - return 0; -} - -static time_t last_logged_failure = 0; - -/************************************************************************* - * - * Function: redis_get_socket - * - * Purpose: Return a REDIS socket from the connection pool - * - *************************************************************************/ -REDISSOCK *redis_get_socket(REDIS_INST *inst) -{ - REDISSOCK *cur, *start; - int tried_to_connect = 0; - int unconnected = 0; - time_t now = time(NULL); - - /* - * Start at the last place we left off. - */ - start = inst->last_used; - if (!start) start = inst->redispool; - - cur = start; - - while (cur) { -#ifdef HAVE_PTHREAD_H - /* - * If this socket is in use by another thread, - * skip it, and try another socket. - * - * If it isn't used, then grab it ourselves. - */ - if (pthread_mutex_trylock(&cur->mutex) != 0) { - goto next; - } /* else we now have the lock */ -#endif - - /* - * If the socket has outlived its lifetime, and - * is connected, close it, and mark it as open for - * reconnections. - */ - if (inst->lifetime && (cur->state == sockconnected) && - ((cur->connected + inst->lifetime) < now)) { - DEBUG2("Closing socket %d as its lifetime has been exceeded", cur->id); - redis_close_socket(inst, cur); - cur->state = sockunconnected; - goto reconnect; - } - - /* - * If we have performed too many queries over this - * socket, then close it. - */ - if (inst->max_queries && (cur->state == sockconnected) && - (cur->queries >= inst->max_queries)) { - DEBUG2("Closing socket %d as its max_queries has been exceeded", cur->id); - redis_close_socket(inst, cur); - cur->state = sockunconnected; - goto reconnect; - } - - /* - * If we happen upon an unconnected socket, and - * this instance's grace period on - * (re)connecting has expired, then try to - * connect it. This should be really rare. - */ - if ((cur->state == sockunconnected) && (now > inst->connect_after)) { - reconnect: - radlog(L_INFO, "rlm_redis (%s): Trying to (re)connect unconnected handle %d..", inst->xlat_name, cur->id); - tried_to_connect++; - connect_single_socket(inst, cur); - } - - /* if we still aren't connected, ignore this handle */ - if (cur->state == sockunconnected) { - DEBUG("rlm_redis (%s): Ignoring unconnected handle %d..", inst->xlat_name, cur->id); - unconnected++; -#ifdef HAVE_PTHREAD_H - pthread_mutex_unlock(&cur->mutex); -#endif - goto next; - } - - /* should be connected, grab it */ - DEBUG("rlm_redis (%s): Reserving redis socket id: %d", - inst->xlat_name, cur->id); - - if (unconnected != 0 || tried_to_connect != 0) { - DEBUG("rlm_redis (%s): got socket %d after skipping %d unconnected handles, tried to reconnect %d though", - inst->xlat_name, cur->id, unconnected, tried_to_connect); - } - - /* - * The socket is returned in the locked - * state. - * - * We also remember where we left off, - * so that the next search can start from - * here. - * - * Note that multiple threads MAY over-write - * the 'inst->last_used' variable. This is OK, - * as it's a pointer only used for reading. - */ - inst->last_used = cur->next; - cur->queries++; - return cur; - - /* move along the list */ - next: - cur = cur->next; - - /* - * Because we didnt start at the start, once we - * hit the end of the linklist, we should go - * back to the beginning and work toward the - * middle! - */ - if (!cur) { - cur = inst->redispool; - } - - /* - * If we're at the socket we started - */ - if (cur == start) { - break; - } - } - - /* - * Suppress most of the log messages. We don't want to - * flood the log with this message for EVERY packet. - * Instead, write to the log only once a second or so. - * - * This code has race conditions when threaded, but the - * only result is that a few more messages are logged. - */ - if (now <= last_logged_failure) return NULL; - last_logged_failure = now; - - /* We get here if every DB handle is unconnected and unconnectABLE */ - radlog(L_INFO, "rlm_redis (%s): There are no DB handles to use! skipped %d, tried to connect %d", - inst->xlat_name, unconnected, tried_to_connect); - return NULL; -} - -/************************************************************************* - * - * Function: redis_release_socket - * - * Purpose: Frees a REDIS socket back to the connection pool - * - *************************************************************************/ -int redis_release_socket(REDIS_INST *inst, REDISSOCK *dissocket) -{ - -#ifdef HAVE_PTHREAD_H - pthread_mutex_unlock(&dissocket->mutex); -#endif - - radlog(L_DBG, "rlm_redis (%s): Released redis socket id: %d", - inst->xlat_name, dissocket->id); - + freeReplyObject(dissocket->reply); + dissocket->reply = NULL; return 0; } @@ -606,15 +337,16 @@ static int redis_instantiate(CONF_SECTION *conf, void **instance) inst->xlat_name = strdup(xlat_name); xlat_register(inst->xlat_name, (RAD_XLAT_FUNC)redis_xlat, inst); - if (redis_init_socketpool(inst) < 0) { + inst->pool = fr_connection_pool_init(conf, inst, + redis_create_conn, NULL, + redis_delete_conn); + if (!inst->pool) { redis_detach(inst); return -1; } inst->redis_query = rlm_redis_query; inst->redis_finish_query = rlm_redis_finish_query; - inst->redis_get_socket = redis_get_socket; - inst->redis_release_socket = redis_release_socket; inst->redis_escape_func = redis_escape_func; *instance = inst; diff --git a/src/modules/rlm_redis/rlm_redis.h b/src/modules/rlm_redis/rlm_redis.h index 1bde5186d39..6f073059f36 100644 --- a/src/modules/rlm_redis/rlm_redis.h +++ b/src/modules/rlm_redis/rlm_redis.h @@ -34,43 +34,22 @@ RCSIDH(rlm_redis_h, "$Id$") #include #include -typedef struct redis_socket { - int id; - -#ifdef HAVE_PTHREAD_H - pthread_mutex_t mutex; -#endif - struct redis_socket *next; - enum { sockconnected, sockunconnected } state; - +typedef struct redis_socket_t { redisContext *conn; redisReply *reply; - - time_t connected; - int queries; } REDISSOCK; typedef struct rlm_redis_t REDIS_INST; typedef struct rlm_redis_t { - time_t connect_after; - REDISSOCK *redispool; - REDISSOCK *last_used; - char *xlat_name; - int numconnections; - int connect_failure_retry_delay; - int lifetime; - int max_queries; - char *hostname; int port; char *password; + fr_connection_pool_t *pool; - REDISSOCK *(*redis_get_socket)(REDIS_INST * inst); - int (*redis_release_socket)(REDIS_INST * inst, REDISSOCK *dissocket); - int (*redis_query)(REDISSOCK *dissocket, REDIS_INST *inst, char *query); + int (*redis_query)(REDISSOCK **dissocket_p, REDIS_INST *inst, char *query); int (*redis_finish_query)(REDISSOCK *dissocket); size_t (*redis_escape_func)(char *out, size_t outlen, const char *in); @@ -78,11 +57,8 @@ typedef struct rlm_redis_t { #define MAX_QUERY_LEN 4096 -int rlm_redis_query(REDISSOCK *dissocket, REDIS_INST *inst, char *query); +int rlm_redis_query(REDISSOCK **dissocket_p, REDIS_INST *inst, char *query); int rlm_redis_finish_query(REDISSOCK *dissocket); -REDISSOCK * redis_get_socket(REDIS_INST * inst); -int redis_release_socket(REDIS_INST * inst, REDISSOCK *dissocket); - #endif /* RLM_REDIS_H */ diff --git a/src/modules/rlm_rediswho/rlm_rediswho.c b/src/modules/rlm_rediswho/rlm_rediswho.c index 68ec8fb4d77..d517c29ffed 100644 --- a/src/modules/rlm_rediswho/rlm_rediswho.c +++ b/src/modules/rlm_rediswho/rlm_rediswho.c @@ -33,6 +33,7 @@ RCSID("$Id$") typedef struct rlm_rediswho_t { const char *xlat_name; + CONF_SECTION *cs; char *redis_instance_name; REDIS_INST *redis_inst; @@ -46,59 +47,21 @@ typedef struct rlm_rediswho_t { * How many session updates to keep track of per user */ int trim_count; - - char *start_insert; - char *start_trim; - char *start_expire; - - char *alive_insert; - char *alive_trim; - char *alive_expire; - - char *stop_insert; - char *stop_trim; - char *stop_expire; } rlm_rediswho_t; static CONF_PARSER module_config[] = { { "redis-instance-name", PW_TYPE_STRING_PTR, offsetof(rlm_rediswho_t, redis_instance_name), NULL, "redis"}, - - { "expiry-time", PW_TYPE_INTEGER, - offsetof(rlm_rediswho_t, expiry_time), NULL, "86400"}, - { "trim-count", PW_TYPE_INTEGER, - offsetof(rlm_rediswho_t, trim_count), NULL, "100"}, - - { "start-insert", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, start_insert), NULL, ""}, - { "start-trim", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, start_trim), NULL, ""}, - { "start-expire", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, start_expire), NULL, ""}, - - { "alive-insert", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, alive_insert), NULL, ""}, - { "alive-trim", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, alive_trim), NULL, ""}, - { "alive-expire", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, alive_expire), NULL, ""}, - - { "stop-insert", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, stop_insert), NULL, ""}, - { "stop-trim", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, stop_trim), NULL, ""}, - { "stop-expire", PW_TYPE_STRING_PTR, - offsetof(rlm_rediswho_t, stop_expire), NULL, ""}, - { NULL, -1, 0, NULL, NULL} }; /* * Query the database executing a command with no result rows */ -static int rediswho_command(const char *fmt, REDISSOCK *dissocket, - rlm_rediswho_t *data, REQUEST *request) +static int rediswho_command(const char *fmt, REDISSOCK **dissocket_p, + rlm_rediswho_t *inst, REQUEST *request) { + REDISSOCK *dissocket; char query[MAX_STRING_LEN * 4]; /* @@ -114,12 +77,13 @@ static int rediswho_command(const char *fmt, REDISSOCK *dissocket, strcpy(query, fmt); } - if (data->redis_inst->redis_query(dissocket, data->redis_inst, query) < 0) { + if (inst->redis_inst->redis_query(dissocket_p, inst->redis_inst, query) < 0) { radlog(L_ERR, "rediswho_command: database query error in: '%s'", query); - return 0; + return -1; } + dissocket = *dissocket_p; switch (dissocket->reply->type) { case REDIS_REPLY_INTEGER: @@ -135,7 +99,7 @@ static int rediswho_command(const char *fmt, REDISSOCK *dissocket, break; } - (data->redis_inst->redis_finish_query)(dissocket); + (inst->redis_inst->redis_finish_query)(dissocket); return 0; } @@ -176,59 +140,7 @@ static int rediswho_instantiate(CONF_SECTION * conf, void ** instance) inst->xlat_name = cf_section_name1(conf); inst->xlat_name = strdup(inst->xlat_name); - - DEBUG("xlat name %s\n", inst->xlat_name); - - /* - * Check that all the queries are in place - */ - if ((inst->start_insert == NULL) || !*inst->start_insert) { - radlog(L_ERR, "rlm_rediswho: the 'start_insert' statement must be set."); - rediswho_detach(inst); - return -1; - } - if ((inst->start_trim == NULL) || !*inst->start_trim) { - radlog(L_ERR, "rlm_rediswho: the 'start_trim' statement must be set."); - rediswho_detach(inst); - return -1; - } - if ((inst->start_expire == NULL) || !*inst->start_expire) { - radlog(L_ERR, "rlm_rediswho: the 'start_expire' statement must be set."); - rediswho_detach(inst); - return -1; - } - - if ((inst->alive_insert == NULL) || !*inst->alive_insert) { - radlog(L_ERR, "rlm_rediswho: the 'alive_insert' statement must be set."); - rediswho_detach(inst); - return -1; - } - if ((inst->alive_trim == NULL) || !*inst->alive_trim) { - radlog(L_ERR, "rlm_rediswho: the 'alive_trim' statement must be set."); - rediswho_detach(inst); - return -1; - } - if ((inst->alive_expire == NULL) || !*inst->alive_expire) { - radlog(L_ERR, "rlm_rediswho: the 'alive_expire' statement must be set."); - rediswho_detach(inst); - return -1; - } - - if ((inst->stop_insert == NULL) || !*inst->stop_insert) { - radlog(L_ERR, "rlm_rediswho: the 'stop_insert' statement must be set."); - rediswho_detach(inst); - return -1; - } - if ((inst->stop_trim == NULL) || !*inst->stop_trim) { - radlog(L_ERR, "rlm_rediswho: the 'stop_trim' statement must be set."); - rediswho_detach(inst); - return -1; - } - if ((inst->stop_expire == NULL) || !*inst->stop_expire) { - radlog(L_ERR, "rlm_rediswho: the 'stop_expire' statement must be set."); - rediswho_detach(inst); - return -1; - } + inst->cs = conf; modinst = find_module_instance(cf_section_find("modules"), inst->redis_instance_name, 1); @@ -255,54 +167,32 @@ static int rediswho_instantiate(CONF_SECTION * conf, void ** instance) return 0; } -static int rediswho_accounting_start(REDISSOCK *dissocket, - rlm_rediswho_t *data, REQUEST *request) +static int rediswho_accounting_all(REDISSOCK **dissocket_p, + rlm_rediswho_t *inst, REQUEST *request, + const char *insert, + const char *trim, + const char *expire) { - rediswho_command(data->start_insert, dissocket, data, request); - - /* Only trim if necessary */ - if (dissocket->reply->type == REDIS_REPLY_INTEGER) { - if (dissocket->reply->integer > data->trim_count) { - rediswho_command(data->start_trim, dissocket, data, request); - } - } - - rediswho_command(data->start_expire, dissocket, data, request); - - return RLM_MODULE_OK; -} + REDISSOCK *dissocket; -static int rediswho_accounting_alive(REDISSOCK *dissocket, - rlm_rediswho_t *data, REQUEST *request) -{ - rediswho_command(data->alive_insert, dissocket, data, request); + if (!insert || !trim || !expire) return 0; - /* Only trim if necessary */ - if (dissocket->reply->type == REDIS_REPLY_INTEGER) { - if (dissocket->reply->integer > data->trim_count) { - rediswho_command(data->alive_trim, dissocket, data, request); - } + if (rediswho_command(insert, dissocket_p, inst, request) < 0) { + return -1; } - rediswho_command(data->alive_expire, dissocket, data, request); - - return RLM_MODULE_OK; -} - -static int rediswho_accounting_stop(REDISSOCK *dissocket, - rlm_rediswho_t *data, REQUEST *request) -{ - - rediswho_command(data->stop_insert, dissocket, data, request); - /* Only trim if necessary */ + dissocket = *dissocket_p; if (dissocket->reply->type == REDIS_REPLY_INTEGER) { - if (dissocket->reply->integer > data->trim_count) { - rediswho_command(data->stop_trim, dissocket, data, request); + if (dissocket->reply->integer > inst->trim_count) { + if (rediswho_command(trim, dissocket_p, + inst, request) < 0) { + return -1; + } } } - rediswho_command(data->stop_expire, dissocket, data, request); + rediswho_command(expire, dissocket_p, inst, request); return RLM_MODULE_OK; } @@ -311,8 +201,10 @@ static int rediswho_accounting(void * instance, REQUEST * request) { int rcode; VALUE_PAIR * vp; - int acct_status_type; - rlm_rediswho_t * data = (rlm_rediswho_t *) instance; + DICT_VALUE *dv; + CONF_SECTION *cs; + const char *insert, *trim, *expire; + rlm_rediswho_t *inst = (rlm_rediswho_t *) instance; REDISSOCK *dissocket; vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0); @@ -320,56 +212,35 @@ static int rediswho_accounting(void * instance, REQUEST * request) RDEBUG("Could not find account status type in packet."); return RLM_MODULE_NOOP; } - acct_status_type = vp->vp_integer; - - switch (acct_status_type) { - case PW_STATUS_START: - case PW_STATUS_ALIVE: - case PW_STATUS_STOP: - case PW_STATUS_ACCOUNTING_ON: - case PW_STATUS_ACCOUNTING_OFF: - break; - default: - /* We don't care about any other accounting packet */ + dv = dict_valbyattr(vp->attr, vp->vendor, vp->vp_integer); + if (!dv) { + RDEBUG("Unknown Acct-Status-Type %u", vp->vp_integer); return RLM_MODULE_NOOP; } - /* - * Some nonsensical security checks. - */ - if (!request->username) { - RDEBUG("User-Name is required"); + cs = cf_section_sub_find(inst->cs, dv->name); + if (!cs) { + RDEBUG("No subsection %s", dv->name); return RLM_MODULE_NOOP; } - dissocket = data->redis_inst->redis_get_socket(data->redis_inst); - if (dissocket == NULL) { + dissocket = fr_connection_get(inst->redis_inst->pool); + if (!dissocket) { RDEBUG("cannot allocate redis connection"); return RLM_MODULE_FAIL; } - switch (acct_status_type) { - case PW_STATUS_START: - rcode = rediswho_accounting_start(dissocket, data, request); - break; + insert = cf_pair_value(cf_pair_find(cs, "insert"); + trim = cf_pair_value(cf_pair_find(cs, "trim"); + expire = cf_pair_value(cf_pair_find(cs, "expire"); - case PW_STATUS_ALIVE: - rcode = rediswho_accounting_alive(dissocket, data, request); - break; - - case PW_STATUS_STOP: - rcode = rediswho_accounting_stop(dissocket, data, request); - break; - - case PW_STATUS_ACCOUNTING_ON: - case PW_STATUS_ACCOUNTING_OFF: - /* TODO */ - break; - - } + rcode = rediswho_accounting_all(&dissocket, inst, request, + inst->start_insert, + inst->start_trim, + inst->start_expire); - data->redis_inst->redis_release_socket(data->redis_inst, dissocket); + if (dissocket) fr_connection_release(inst->redis_inst->pool, dissocket); return rcode; }