]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
rlm_always: xlat for setting module status and rcode via unlang (#3396)
authorTerry Burton <tez@terryburton.co.uk>
Tue, 21 Apr 2020 21:03:17 +0000 (22:03 +0100)
committerGitHub <noreply@github.com>
Tue, 21 Apr 2020 21:03:17 +0000 (16:03 -0500)
Parity with: radmin -e 'set module status db_status {alive,dead,notfound,...}'

raddb/mods-available/always
src/modules/rlm_always/rlm_always.c
src/tests/modules/always/module.conf
src/tests/modules/always/set_rcode.unlang [new file with mode: 0644]
src/tests/modules/always/set_status_dead.unlang [new file with mode: 0644]
src/tests/modules/always/set_status_revive.unlang [new file with mode: 0644]

index de3f13089d8951f4c822ebc4007df58e0487de14..92e892768c61bb8e2357ac6935659cb4c5fa9721 100644 (file)
 #
 #  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
 }
index 6a75164a89db3df1b00eabeaf95906b75043c4cd..228bff971062a551db44f8868b404efe72baede6 100644 (file)
@@ -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>
 
 /*
@@ -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
         */
@@ -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,
index ce784b16ee815e027a4c0b785f8cafbbc65a8791..39995e501ac964cbe637f1fc8128f55d7af39595 100644 (file)
@@ -1,3 +1,7 @@
 always my_reject {
        rcode = reject
-}
\ No newline at end of file
+}
+
+always db_status {
+       rcode = ok
+}
diff --git a/src/tests/modules/always/set_rcode.unlang b/src/tests/modules/always/set_rcode.unlang
new file mode 100644 (file)
index 0000000..faaed28
--- /dev/null
@@ -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"
+}
diff --git a/src/tests/modules/always/set_status_dead.unlang b/src/tests/modules/always/set_status_dead.unlang
new file mode 100644 (file)
index 0000000..6b29ede
--- /dev/null
@@ -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"
+}
diff --git a/src/tests/modules/always/set_status_revive.unlang b/src/tests/modules/always/set_status_revive.unlang
new file mode 100644 (file)
index 0000000..3e71d39
--- /dev/null
@@ -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"
+}