summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_rediswho/rlm_rediswho.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_rediswho/rlm_rediswho.c')
-rw-r--r--src/modules/rlm_rediswho/rlm_rediswho.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/modules/rlm_rediswho/rlm_rediswho.c b/src/modules/rlm_rediswho/rlm_rediswho.c
new file mode 100644
index 0000000..11c3cf4
--- /dev/null
+++ b/src/modules/rlm_rediswho/rlm_rediswho.c
@@ -0,0 +1,247 @@
+/*
+ * 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_rediswho.c
+ * @brief Session tracking using redis.
+ *
+ * @copyright 2000,2006 The FreeRADIUS server project
+ * @copyright 2011 TekSavvy Solutions <gabe@teksavvy.com>
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+
+#include <ctype.h>
+
+#include <rlm_redis.h>
+
+typedef struct rlm_rediswho_t {
+ char const *xlat_name;
+ CONF_SECTION *cs;
+
+ char const *redis_instance_name;
+ REDIS_INST *redis_inst;
+
+ /*
+ * expiry time in seconds if no updates are received for a user
+ */
+ int expiry_time;
+
+ /*
+ * How many session updates to keep track of per user
+ */
+ int trim_count;
+
+ /*
+ * These are used only for parsing. They aren't used at run-time.
+ */
+ char const *insert;
+ char const *trim;
+ char const *expire;
+
+} rlm_rediswho_t;
+
+static CONF_PARSER section_config[] = {
+ { "insert", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, insert), NULL },
+ { "trim", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_rediswho_t, trim), NULL }, /* required only if trim_count > 0 */
+ { "expire", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, expire), NULL },
+
+ CONF_PARSER_TERMINATOR
+};
+
+static CONF_PARSER module_config[] = {
+ { "redis-instance-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_rediswho_t, redis_instance_name), NULL },
+ { "redis_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rediswho_t, redis_instance_name), "redis" },
+
+ { "trim-count", FR_CONF_OFFSET(PW_TYPE_SIGNED | PW_TYPE_DEPRECATED, rlm_rediswho_t, trim_count), NULL },
+ { "trim_count", FR_CONF_OFFSET(PW_TYPE_SIGNED, rlm_rediswho_t, trim_count), "-1" },
+
+ /*
+ * These all smash the same variables, because we don't care about them right now.
+ * In 3.1, we should have a way of saying "parse a set of sub-sections according to a template"
+ */
+ { "Start", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), section_config },
+ { "Interim-Update", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), section_config },
+ { "Stop", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), section_config },
+
+ CONF_PARSER_TERMINATOR
+};
+
+/*
+ * Query the database executing a command with no result rows
+ */
+static int rediswho_command(char const *fmt, REDISSOCK **dissocket_p,
+ rlm_rediswho_t *inst, REQUEST *request)
+{
+ REDISSOCK *dissocket;
+ int result = 0;
+
+ if (!fmt) {
+ return 0;
+ }
+
+ if (inst->redis_inst->redis_query(dissocket_p, inst->redis_inst,
+ fmt, request) < 0) {
+
+ ERROR("rediswho_command: database query error in: '%s'", fmt);
+ return -1;
+
+ }
+ dissocket = *dissocket_p;
+
+ switch (dissocket->reply->type) {
+ case REDIS_REPLY_INTEGER:
+ DEBUG("rediswho_command: query response %lld\n",
+ dissocket->reply->integer);
+ if (dissocket->reply->integer > 0)
+ result = dissocket->reply->integer;
+ break;
+
+ case REDIS_REPLY_STATUS:
+ case REDIS_REPLY_STRING:
+ DEBUG("rediswho_command: query response %s\n",
+ dissocket->reply->str);
+ break;
+
+ default:
+ break;
+ }
+
+ (inst->redis_inst->redis_finish_query)(dissocket);
+
+ return result;
+}
+
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+ module_instance_t *modinst;
+ rlm_rediswho_t *inst = instance;
+
+ inst->xlat_name = cf_section_name2(conf);
+
+ if (!inst->xlat_name)
+ inst->xlat_name = cf_section_name1(conf);
+
+ inst->cs = conf;
+
+ modinst = module_instantiate(cf_section_find("modules"),
+ inst->redis_instance_name);
+ if (!modinst) {
+ ERROR("rediswho: failed to find module instance \"%s\"",
+ inst->redis_instance_name);
+ return -1;
+ }
+
+ if (strcmp(modinst->entry->name, "rlm_redis") != 0) {
+ ERROR("rediswho: Module \"%s\""
+ " is not an instance of the redis module",
+ inst->redis_instance_name);
+ return -1;
+ }
+
+ inst->redis_inst = (REDIS_INST *) modinst->insthandle;
+
+ return 0;
+}
+
+static int mod_accounting_all(REDISSOCK **dissocket_p,
+ rlm_rediswho_t *inst, REQUEST *request,
+ char const *insert,
+ char const *trim,
+ char const *expire)
+{
+ int result;
+
+ result = rediswho_command(insert, dissocket_p, inst, request);
+ if (result < 0) {
+ return RLM_MODULE_FAIL;
+ }
+
+ /* Only trim if necessary */
+ if (inst->trim_count >= 0 && result > inst->trim_count) {
+ if (rediswho_command(trim, dissocket_p,
+ inst, request) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+ }
+
+ if (rediswho_command(expire, dissocket_p, inst, request) < 0) {
+ return RLM_MODULE_FAIL;
+ }
+
+ return RLM_MODULE_OK;
+}
+
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void * instance, REQUEST * request)
+{
+ rlm_rcode_t rcode;
+ VALUE_PAIR * vp;
+ DICT_VALUE *dv;
+ CONF_SECTION *cs;
+ char const *insert, *trim, *expire;
+ rlm_rediswho_t *inst = (rlm_rediswho_t *) instance;
+ REDISSOCK *dissocket;
+
+ vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
+ if (!vp) {
+ RDEBUG("Could not find account status type in packet");
+ return RLM_MODULE_NOOP;
+ }
+
+ dv = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
+ if (!dv) {
+ RDEBUG("Unknown Acct-Status-Type %u", vp->vp_integer);
+ return RLM_MODULE_NOOP;
+ }
+
+ cs = cf_section_sub_find(inst->cs, dv->name);
+ if (!cs) {
+ RDEBUG("No subsection %s", dv->name);
+ return RLM_MODULE_NOOP;
+ }
+
+ dissocket = fr_connection_get(inst->redis_inst->pool);
+ if (!dissocket) return RLM_MODULE_FAIL;
+
+ 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"));
+
+ rcode = mod_accounting_all(&dissocket, inst, request,
+ insert,
+ trim,
+ expire);
+
+ if (dissocket) fr_connection_release(inst->redis_inst->pool, dissocket);
+
+ return rcode;
+}
+
+extern module_t rlm_rediswho;
+module_t rlm_rediswho = {
+ .magic = RLM_MODULE_INIT,
+ .name = "rediswho",
+ .type = RLM_TYPE_THREAD_SAFE,
+ .inst_size = sizeof(rlm_rediswho_t),
+ .config = module_config,
+ .instantiate = mod_instantiate,
+ .methods = {
+ [MOD_ACCOUNTING] = mod_accounting
+ },
+};