]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Clean up for 3.0
authorAlan T. DeKok <aland@freeradius.org>
Sun, 13 Nov 2011 12:02:21 +0000 (13:02 +0100)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 14 Nov 2011 14:59:43 +0000 (15:59 +0100)
Use new connection pool API.  Document it.
Use a more consistent module configuration

Less code, and it does more

raddb/modules/redis
raddb/modules/rediswho
src/modules/rlm_redis/rlm_redis.c
src/modules/rlm_redis/rlm_redis.h
src/modules/rlm_rediswho/rlm_rediswho.c

index d7605d9888607aa6451ab24450cebfd7bc9d4437..600aaa2c93d2586b1dc7f059fdff39ff964c24f2 100644 (file)
@@ -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
+       }
+}
index 470dbadcad30f6acde13f44bab7ee5a59e128a83..8963af049e3e13e618ba5ce11c11658dc8ccf5af 100644 (file)
@@ -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}"
+       }
 }
index 515533e1c250d740a05082fbaa4173091cafe14c..144f319faea8f24cbad0f72d671cce0d512e1b0a 100644 (file)
@@ -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;
index 1bde5186d39bcd4be8231c2824f382daa2596001..6f073059f369cb1fc1e2fd8b138ed48df151d166 100644 (file)
@@ -34,43 +34,22 @@ RCSIDH(rlm_redis_h, "$Id$")
 #include <freeradius-devel/modpriv.h>
 #include <hiredis/hiredis.h>
 
-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 */
 
index 68ec8fb4d77f60b919c9588709ae08093a467978..d517c29ffedcc134598fa7134f88ad2fbcdd4bdc 100644 (file)
@@ -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;
 }