Skip to content

Commit

Permalink
Final set of changes for making redundant xlat function registration …
Browse files Browse the repository at this point in the history
…not suck

We now group module xlats by name (all self-named module expansions are considered equal).

This allows:

redundant {
    sql
    linelog
}

Which would issue a query against SQL, and then log it to disk.

The instantiate code catches/disallows calls to incompatible module expansions in much the same way as C's _Generic does...
  • Loading branch information
arr2036 committed Jun 20, 2024
1 parent e3abd16 commit 9f0a45f
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 118 deletions.
12 changes: 6 additions & 6 deletions doc/antora/modules/reference/pages/unlang/load-balance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ places. Instead of copying the same text multiple times, a
directory.

For example, the following text can be placed into the file
`mods-enabled/sql123`. Once it is there, it can be used as a module
named `sql123`, and used anywhere a module is allowed to use.
`mods-enabled/sql_all`. Once it is there, it can be used as a module
named `sql_all`, and used anywhere a module is allowed to use.

.Example of Load-Balance SQL module
[source,unlang]
----
load-balance sql123 {
load-balance sql_all {
sql1
sql2
sql3
Expand All @@ -61,18 +61,18 @@ load-balance sql123 {

In previous versions of the server, this definition would be placed
into the `instantiate` section of `radiusd.conf. This configuration
is no longer used, and the `sql123` definition can just be placed as
is no longer used, and the `sql_all` definition can just be placed as
a module definition into the `mods-enabled/` directory.

== Load-Balance Expansions

When the `sql123` module is defined as above, it can also be used as
When the `sql_all` module is defined as above, it can also be used as
in a xref:xlat/index.adoc[dynamic expansion]:

.Example of Load-Balance SQL module
[source,unlang]
----
&Reply-Message := %sql123("SELECT message FROM table WHERE name='%{User-Name}'")
&Reply-Message := %sql_all("SELECT message FROM table WHERE name='%{User-Name}'")
}
----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ places. Instead of copying the same text multiple times, a
directory.

For example, the following text can be placed into the file
`mods-enabled/sql123`. Once it is there, it can be used as a module
named `sql123`, and used anywhere a module is allowed to use.
`mods-enabled/sql_all`. Once it is there, it can be used as a module
named `sql_all`, and used anywhere a module is allowed to use.

.Example of Redundant-Load-Balance SQL module
[source,unlang]
----
redundant-load-balance sql123 {
redundant-load-balance sql_all {
sql1
sql2
sql3
Expand All @@ -68,18 +68,18 @@ redundant-load-balance sql123 {

In previous versions of the server, this definition would be placed
into the `instantiate` section of `radiusd.conf. This configuration
is no longer used, and the `sql123` definition can just be placed as
is no longer used, and the `sql_all` definition can just be placed as
a module definition into the `mods-enabled/` directory.

== Redundant-Load-Balance Expansions

When the `sql123` module is defined as above, it can also be used as
When the `sql_all` module is defined as above, it can also be used as
in a xref:xlat/index.adoc[dynamic expansion]:

.Example of Redundant-Load-Balance SQL module
[source,unlang]
----
&Reply-Message := %sql123("SELECT message FROM table WHERE name='%{User-Name}'")
&Reply-Message := %sql_all("SELECT message FROM table WHERE name='%{User-Name}'")
}
----

Expand Down
93 changes: 82 additions & 11 deletions doc/antora/modules/reference/pages/unlang/redundant.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ places. Instead of copying the same text multiple times, a
directory.

For example, the following text can be placed into the file
`mods-enabled/sql123`. Once it is there, it can be used as a module
named `sql123`, and used anywhere a module is allowed to use.
`mods-enabled/sql_all`. Once it is there, it can be used as a module
named `sql_all`, and used anywhere a module is allowed to use.

.Example of Redundant SQL module
[source,unlang]
----
redundant sql123 {
redundant sql_all {
sql1
sql2
sql3
Expand All @@ -61,30 +61,101 @@ redundant sql123 {

In previous versions of the server, this definition would be placed
into the `instantiate` section of `radiusd.conf. This configuration
is no longer used, and the `sql123` definition can just be placed as
is no longer used, and the `sql_all` definition can just be placed as
a module definition into the `mods-enabled/` directory.

== Redundant Expansions

When the `sql123` module is defined as above, it can also be used as
in a xref:xlat/index.adoc[dynamic expansion]:
Modules may be used in a `redundant` section, and modules can register
xref:xlat/index.adoc[dynamic expansions]. It is therefore also useful
to define redundant dynamic expansions. The good news is that the
server does this automatically.

.Example of Redundant SQL module
When a `redundant` section is defined, _and_ where all of the modules
in the `redundant` section reference the same underlying module
(e.g. `sql`, or `ldap`), the server then automatically registers a new
_redundant expansion_, with the name of the `redundant` section. The
example below shows how this works.

.Example
[source,unlang]
----
&Reply-Message := %sql123("SELECT message FROM table WHERE name='%{User-Name}'")
redundant sql_all {
sql1
sql2
sql3
}
----

The expansion works exactly like a `redundant` block. First `sql1` is
tried. If that fails, `sql2` is tried. Then if that fails, `sql3` is
tried.
Each `sql` module will register it's own xref:xlat/index.adoc[dynamic
expansions], such as `%sql1(...)`, and `%sql2(...)` and `%sql3(...).
However, there will _also_ be a new SQL expansion defined:

----
%sql_all(...)
----

This is a _redundant_ expansion. When it is called, it follows
operation of the `redundant` block which defined it. That is, it will
first run `%sql1(...)`, then if that fails, run `%sql2(...)`, etc.

.Example of Redundant SQL module
[source,unlang]
----
&Reply-Message := %sql_all("SELECT message FROM table WHERE name='%{User-Name}'")
----

The one caveat here is that the arguments passed to the underlying
modules are expanded again _for each call_. If the expansion
arguments have side effects, then those side effects can be applied
multiple times, once for each `redundant` attempt.

=== Additional redundant expansions

Modules can register multiple expansions. If the module's expansion
shares the module's instance name, the redundant expansion is
registered with the `redundant` section name. Otherwise, the redundant
expansion is _qualified_ with the `redundant` section name.

For example, the `sql` module provides two expansions, a self-named
expansion and a `group` expansion. Using the example from the previous
section, `redundant sql_all {...}` would register the following:

----
%sql_all(...)
%sql_all.group(...)
----

=== Module expansion grouping

Module expansions which share the same name are grouped into the same
redundant expansion. This may result in only a subset of the module
expansions in a `redundant` section being called by a redundant
expansion.

.Example
[source,unlang]
----
redundant sql_and_ldap {
sql1
sql2
ldap1
ldap2
}
----

The `sql_and_ldap` redundant section would register the following
expansions:

- `%sql_and_ldap(...)` which would call `%sql1(...)`, `%sql2(...)`, `%ldap1(...)`, and `%ldap2(...)`
- `%sql_and_ldap.group(...)` which would call `%sql1(...)`, `%sql2(...)`
- `%sql_and_ldap.memberof(...)` which would call `%ldap1(...)`, `%ldap2(...)`
- `%sql_and_ldap.profile(...)` which would call `%ldap1(...)`, `%ldap2(...)`

If the arguments passed to the redundant expansion are not compatible
with _all_ the module expansions it would call, the server will log an
error and either fail to start, or stop processing the expression which
called the redundant expansion.

// Copyright (C) 2021 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
// This documentation was developed by Network RADIUS SAS.
15 changes: 3 additions & 12 deletions src/lib/server/module_rlm.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ xlat_t *module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx,
mrx->xlat = x;
mrx->mi = mi;

fr_rb_insert(&mri->xlats, mrx);
fr_dlist_insert_tail(&mri->xlats, mrx);

return x;
}
Expand Down Expand Up @@ -1100,16 +1100,6 @@ static int module_method_validate(module_instance_t *mi)
return module_method_group_validate(&mrlm->method);
}

/** Compare xlat functions registered to a module
*/
static int8_t module_rlm_xlat_cmp(void const *one, void const *two)
{
module_rlm_xlat_t const *a = one;
module_rlm_xlat_t const *b = two;

return xlat_func_cmp(a->xlat, b->xlat);
}

/** Allocate a rlm module instance
*
* These have extra space allocated to hold the dlist of associated xlats.
Expand Down Expand Up @@ -1138,7 +1128,8 @@ module_instance_t *module_rlm_instance_alloc(module_list_t *ml,

MEM(mri = talloc(mi, module_rlm_instance_t));
module_instance_uctx_set(mi, mri);
fr_rb_inline_init(&mri->xlats, module_rlm_xlat_t, node, module_rlm_xlat_cmp, NULL);

fr_dlist_talloc_init(&mri->xlats, module_rlm_xlat_t, entry);

return mi;
}
Expand Down
6 changes: 3 additions & 3 deletions src/lib/server/module_rlm.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct module_rlm_s {
};

struct module_rlm_instance_s {
fr_rb_tree_t xlats; //!< xlats registered to this module instance.
fr_dlist_head_t xlats; //!< xlats registered to this module instance.
///< This is used by the redundant/loadbalance
///< xlats to register versions of the xlats
///< exported by the module instances.
Expand All @@ -51,8 +51,8 @@ struct module_rlm_instance_s {
*/
typedef struct {
xlat_t const *xlat; //!< The xlat function.
module_instance_t const *mi; //!< The module instance that registered the xlat.
fr_rb_node_t node; //!< Entry in an rbtree of registered xlats.
module_instance_t *mi; //!< The module instance that registered the xlat.
fr_dlist_t entry; //!< Entry in a linked list of registered xlats.
} module_rlm_xlat_t;

/** The output of module_rlm_by_name_and_method
Expand Down
Loading

0 comments on commit 9f0a45f

Please sign in to comment.