Skip to content

Commit

Permalink
rlm_always: xlat for setting module status and rcode via unlang (#3396)
Browse files Browse the repository at this point in the history
Parity with: radmin -e 'set module status db_status {alive,dead,notfound,...}'
  • Loading branch information
terryburton committed Apr 21, 2020
1 parent 26ca6af commit 25142b0
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 3 deletions.
20 changes: 20 additions & 0 deletions raddb/mods-available/always
Expand Up @@ -32,6 +32,26 @@
#
# mpp = <integer>
#
# An xlat based on the instance name can be called to change the status
# returned by the instance.
#
# Force the module status to be alive or dead:
#
# %{db_status:alive}
# %{db_status:dead}
#
# Update the rcode returned by an alive module (a dead module returns fail):
#
# %{db_status:ok}
# %{db_status:fail}
# %{db_status:notfound}
# ...
#
# The above xlats expand to the current status of the module. To fetch the
# current status without affecting it call the xlat with an empty argument:
#
# %{db_status:}
#
always reject {
rcode = reject
}
Expand Down
88 changes: 86 additions & 2 deletions src/modules/rlm_always/rlm_always.c
Expand Up @@ -26,6 +26,7 @@ RCSID("$Id$")

#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/modpriv.h>
#include <freeradius-devel/modcall.h>

/*
Expand All @@ -52,12 +53,94 @@ static const CONF_PARSER module_config[] = {
CONF_PARSER_TERMINATOR
};

/** Set module status or rcode
*
* Look ma, no locks...
*
* Example: "%{db_status:dead}"
*/
static ssize_t always_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen)
{
CONF_SECTION *cs;
module_instance_t *mi;
rlm_always_t *inst = instance;
char const *status = fmt;
char const *p;
size_t len;

cs = cf_section_find("modules");
if (!cs) return -1;

mi = module_find(cs, inst->name);
if (!mi) {
RERROR("Can't find the module that registered this xlat: %s", inst->name);
return -1;
}

/*
* Expand to the existing status
*/
p = "alive";
if (mi->force) {
p = fr_int2str(mod_rcode_table, mi->code, "<invalid>");
}

len = strlen(p);
if (outlen < len) {
RWARN("Output is too short!");
*out = '\0';
} else {
strncpy(out, p, outlen);
}

if (*fmt == '\0') goto done;

/*
* Set the module status
*/
if (strcmp(status, "alive") == 0) {
mi->force = false;

} else if (strcmp(status, "dead") == 0) {
mi->code = RLM_MODULE_FAIL;
mi->force = true;

} else {
int rcode;

rcode = fr_str2int(mod_rcode_table, status, -1);
if (rcode < 0) {
RWARN("Unknown status \"%s\"", status);
return -1;
}

mi->code = rcode;
mi->force = true;

}

done:
return strlen(out);
}

static int mod_bootstrap(CONF_SECTION *conf, void *instance)
{
rlm_always_t *inst = instance;

inst->name = cf_section_name2(conf);
if (!inst->name) {
inst->name = cf_section_name1(conf);
}

xlat_register(inst->name, always_xlat, NULL, inst);

return 0;
}

static int mod_instantiate(CONF_SECTION *conf, void *instance)
{
rlm_always_t *inst = instance;

inst->name = cf_section_name1(conf);
if (!inst->name) inst->name = cf_section_name2(conf);
/*
* Convert the rcode string to an int
*/
Expand Down Expand Up @@ -130,6 +213,7 @@ module_t rlm_always = {
.type = RLM_TYPE_HUP_SAFE,
.inst_size = sizeof(rlm_always_t),
.config = module_config,
.bootstrap = mod_bootstrap,
.instantiate = mod_instantiate,
.methods = {
[MOD_AUTHENTICATE] = mod_always_return,
Expand Down
6 changes: 5 additions & 1 deletion src/tests/modules/always/module.conf
@@ -1,3 +1,7 @@
always my_reject {
rcode = reject
}
}

always db_status {
rcode = ok
}
44 changes: 44 additions & 0 deletions src/tests/modules/always/set_rcode.unlang
@@ -0,0 +1,44 @@
#
# Set status to "notfound". xlat should expand to previous status, "alive"
#
if ("%{db_status:notfound}" != "alive") {
update reply {
Filter-Id += "failed"
}
}


#
# Verify that the status was changed
#
db_status
if (!notfound) {
update reply {
Filter-Id += "failed"
}
}


#
# Fetch status using xlat without setting the status
#
if ("%{db_status:}" != "notfound") {
update reply {
Filter-Id += "failed"
}
}


#
# Verify that the status did not change
#
db_status
if (notfound) {
update reply {
Filter-Id += "success"
}
}

update control {
Cleartext-Password := "hello"
}
18 changes: 18 additions & 0 deletions src/tests/modules/always/set_status_dead.unlang
@@ -0,0 +1,18 @@
#
# Set the module status to dead, call it and check that it fails
#
%{db_status:dead}

db_status {
fail = 1
}

if (fail) {
update reply {
Filter-Id := "success"
}
}

update control {
Cleartext-Password := "hello"
}
28 changes: 28 additions & 0 deletions src/tests/modules/always/set_status_revive.unlang
@@ -0,0 +1,28 @@
#
# Fail a module...
#
%{db_status:dead}
db_status {
fail = 1
}
if (!fail) {
update reply {
Filter-Id += "failed"
}
}


#
# ... Now revive it
#
%{db_status:alive}
db_status
if (ok) {
update reply {
Filter-Id += "success"
}
}

update control {
Cleartext-Password := "hello"
}

0 comments on commit 25142b0

Please sign in to comment.