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.
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.
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
19 * @file rlm_rediswho.c
20 * @brief Session tracking using redis.
22 * @copyright 2000,2006 The FreeRADIUS server project
23 * @copyright 2011 TekSavvy Solutions <gabe@teksavvy.com>
28 #include <freeradius-devel/radiusd.h>
32 #include <rlm_redis.h>
34 typedef struct rlm_rediswho_t {
35 char const *xlat_name;
38 char const *redis_instance_name;
39 REDIS_INST *redis_inst;
42 * expiry time in seconds if no updates are received for a user
47 * How many session updates to keep track of per user
55 static CONF_PARSER module_config[] = {
56 { "redis-instance-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_rediswho_t, redis_instance_name), NULL },
57 { "redis_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rediswho_t, redis_instance_name), "redis" },
59 { "trim-count", FR_CONF_OFFSET(PW_TYPE_SIGNED | PW_TYPE_DEPRECATED, rlm_rediswho_t, trim_count), NULL },
60 { "trim_count", FR_CONF_OFFSET(PW_TYPE_SIGNED, rlm_rediswho_t, trim_count), "-1" },
62 { "insert", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, insert), NULL },
63 { "trim", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_rediswho_t, trim), NULL }, /* required only if trim_count > 0 */
64 { "expire", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_rediswho_t, expire), NULL },
66 { NULL, -1, 0, NULL, NULL}
70 * Query the database executing a command with no result rows
72 static int rediswho_command(char const *fmt, REDISSOCK **dissocket_p,
73 rlm_rediswho_t *inst, REQUEST *request)
82 if (inst->redis_inst->redis_query(dissocket_p, inst->redis_inst,
85 ERROR("rediswho_command: database query error in: '%s'", fmt);
89 dissocket = *dissocket_p;
91 switch (dissocket->reply->type) {
92 case REDIS_REPLY_INTEGER:
93 DEBUG("rediswho_command: query response %lld\n",
94 dissocket->reply->integer);
95 if (dissocket->reply->integer > 0)
96 result = dissocket->reply->integer;
99 case REDIS_REPLY_STATUS:
100 case REDIS_REPLY_STRING:
101 DEBUG("rediswho_command: query response %s\n",
102 dissocket->reply->str);
109 (inst->redis_inst->redis_finish_query)(dissocket);
114 static int mod_instantiate(CONF_SECTION *conf, void *instance)
116 module_instance_t *modinst;
117 rlm_rediswho_t *inst = instance;
119 inst->xlat_name = cf_section_name2(conf);
121 if (!inst->xlat_name)
122 inst->xlat_name = cf_section_name1(conf);
126 modinst = module_instantiate(cf_section_find("modules"),
127 inst->redis_instance_name);
129 ERROR("rediswho: failed to find module instance \"%s\"",
130 inst->redis_instance_name);
134 if (strcmp(modinst->entry->name, "rlm_redis") != 0) {
135 ERROR("rediswho: Module \"%s\""
136 " is not an instance of the redis module",
137 inst->redis_instance_name);
141 inst->redis_inst = (REDIS_INST *) modinst->insthandle;
146 static int mod_accounting_all(REDISSOCK **dissocket_p,
147 rlm_rediswho_t *inst, REQUEST *request)
151 result = rediswho_command(inst->insert, dissocket_p, inst, request);
153 return RLM_MODULE_FAIL;
156 /* Only trim if necessary */
157 if (inst->trim_count >= 0 && result > inst->trim_count) {
158 if (rediswho_command(inst->trim, dissocket_p,
159 inst, request) < 0) {
160 return RLM_MODULE_FAIL;
164 if (rediswho_command(inst->expire, dissocket_p, inst, request) < 0) {
165 return RLM_MODULE_FAIL;
168 return RLM_MODULE_OK;
171 static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void * instance, REQUEST * request)
177 rlm_rediswho_t *inst = (rlm_rediswho_t *) instance;
178 REDISSOCK *dissocket;
180 vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
182 RDEBUG("Could not find account status type in packet");
183 return RLM_MODULE_NOOP;
186 dv = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
188 RDEBUG("Unknown Acct-Status-Type %u", vp->vp_integer);
189 return RLM_MODULE_NOOP;
192 cs = cf_section_sub_find(inst->cs, dv->name);
194 RDEBUG("No subsection %s", dv->name);
195 return RLM_MODULE_NOOP;
198 dissocket = fr_connection_get(inst->redis_inst->pool);
199 if (!dissocket) return RLM_MODULE_FAIL;
201 rcode = mod_accounting_all(&dissocket, inst, request);
203 if (dissocket) fr_connection_release(inst->redis_inst->pool, dissocket);
208 extern module_t rlm_rediswho;
209 module_t rlm_rediswho = {
210 .magic = RLM_MODULE_INIT,
212 .type = RLM_TYPE_THREAD_SAFE,
213 .inst_size = sizeof(rlm_rediswho_t),
214 .config = module_config,
215 .instantiate = mod_instantiate,
217 [MOD_ACCOUNTING] = mod_accounting