]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Create public/private heders for unlang code
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 17 Jan 2018 21:42:52 +0000 (14:42 -0700)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 17 Jan 2018 21:43:00 +0000 (14:43 -0700)
Move operations to basic registration API.  Needs more work, but at least we can now move the xlat stuff out of the interpreter.

src/include/modules.h
src/include/unlang.h [new file with mode: 0644]
src/main/modules.c
src/main/request.c
src/main/unlang_compile.c
src/main/unlang_interpret.c
src/main/unlang_op.c
src/main/unlang_priv.h [new file with mode: 0644]
src/main/virtual_servers.c

index 348372b596cfea469907a61f48306427044bbe5a..f9eac4e20c3b8e363d5134b884cbf7aae98b5549 100644 (file)
@@ -207,112 +207,6 @@ int               virtual_servers_bootstrap(CONF_SECTION *config);
 CONF_SECTION   *virtual_server_find(char const *name);
 void           fr_request_async_bootstrap(REQUEST *request, fr_event_list_t *el); /* for unit_test_module */
 
-/** A callback when the the timeout occurs
- *
- * Used when a module needs wait for an event.
- * Typically the callback is set, and then the module returns unlang_module_yield().
- *
- * @note The callback is automatically removed on unlang_resumable(), i.e. if an event
- *     on a registered FD occurs before the timeout event fires.
- *
- * @param[in] request          the request.
- * @param[in] instance         the module instance.
- * @param[in] thread           data specific to this module instance.
- * @param[in] rctx             a local context for the callback.
- * @param[in] fired            the time the timeout event actually fired.
- */
-typedef        void (*fr_unlang_module_timeout_t)(REQUEST *request, void *instance, void *thread, void *rctx,
-                                          struct timeval *fired);
-
-/** A callback when the FD is ready for reading
- *
- * Used when a module needs to read from an FD.  Typically the callback is set, and then the
- * module returns unlang_module_yield().
- *
- * @note The callback is automatically removed on unlang_resumable(), so
- *
- * @param[in] request          the current request.
- * @param[in] instance         the module instance.
- * @param[in] thread           data specific to this module instance.
- * @param[in] rctx             a local context for the callback.
- * @param[in] fd               the file descriptor.
- */
-typedef void (*fr_unlang_module_fd_event_t)(REQUEST *request, void *instance, void *thread, void *rctx, int fd);
-
-/** A callback for when the request is resumed.
- *
- * The resumed request cannot call the normal "authorize", etc. method.  It needs a separate callback.
- *
- * @param[in] request          the current request.
- * @param[in] instance         The module instance.
- * @param[in] thread           data specific to this module instance.
- * @param[in] rctx             a local context for the callback.
- * @return a normal rlm_rcode_t.
- */
-typedef rlm_rcode_t (*fr_unlang_module_resume_t)(REQUEST *request, void *instance, void *thread, void *rctx);
-
-/** A callback when the request gets a fr_state_action_t.
- *
- * A module may call unlang_yeild(), but still need to do something on FR_ACTION_DUP.  If so, it's
- * set here.
- *
- * @note The callback is automatically removed on unlang_resumable().
- *
- * @param[in] request          The current request.
- * @param[in] instance         The module instance.
- * @param[in] thread           data specific to this module instance.
- * @param[in] rctx             Resume ctx for the callback.
- * @param[in] action           which is signalling the request.
- */
-typedef void (*fr_unlang_module_signal_t)(REQUEST *request, void *instance, void *thread,
-                                         void *rctx, fr_state_action_t action);
-
-/*
- *     In unlang_interpret.c, but here for public consumption.
- */
-void           unlang_push_section(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
-
-rlm_rcode_t    unlang_push_module_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
-                                       REQUEST *request, xlat_exp_t const *xlat,
-                                       fr_unlang_module_resume_t callback,
-                                       fr_unlang_module_signal_t signal_callback, void *uctx);
-
-rlm_rcode_t    unlang_interpret_continue(REQUEST *request);
-
-rlm_rcode_t    unlang_interpret(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
-
-rlm_rcode_t    unlang_interpret_synchronous(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t action);
-
-int            unlang_compile(CONF_SECTION *cs, rlm_components_t component);
-int            unlang_compile_subsection(CONF_SECTION *server_cs, char const *name1, char const *name2, rlm_components_t component);
-
-int            unlang_event_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
-                                        void const *ctx, struct timeval *timeout);
-
-int            unlang_event_fd_add(REQUEST *request,
-                                   fr_unlang_module_fd_event_t read,
-                                   fr_unlang_module_fd_event_t write,
-                                   fr_unlang_module_fd_event_t error,
-                                   void const *ctx, int fd);
-
-int            unlang_event_timeout_delete(REQUEST *request, void const *ctx);
-
-int            unlang_event_fd_delete(REQUEST *request, void const *ctx, int fd);
-
-void           unlang_resumable(REQUEST *request);
-
-void           unlang_signal(REQUEST *request, fr_state_action_t action);
-
-int            unlang_stack_depth(REQUEST *request);
-
-rlm_rcode_t    unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
-                                   fr_unlang_module_signal_t signal_callback, void *ctx);
-xlat_action_t  unlang_xlat_yield(REQUEST *request, xlat_resume_callback_t callback,
-                                 fr_unlang_module_signal_t signal_callback, void *rctx);
-
-
-int            unlang_initialize(void);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/include/unlang.h b/src/include/unlang.h
new file mode 100644 (file)
index 0000000..2520311
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ *  This program 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _FR_UNLANG_H
+#define _FR_UNLANG_H
+/**
+ * $Id$
+ *
+ * @file src/include/unlang.h
+ * @brief Public interface to the interpreter
+ *
+ */
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+
+/** Returned by #unlang_op_t calls, determine the next action of the interpreter
+ *
+ * These deal exclusively with control flow.
+ */
+typedef enum {
+       UNLANG_ACTION_CALCULATE_RESULT = 1,     //!< Calculate a new section #rlm_rcode_t value.
+       UNLANG_ACTION_CONTINUE,                 //!< Execute the next #unlang_t.
+       UNLANG_ACTION_PUSHED_CHILD,             //!< #unlang_t pushed a new child onto the stack,
+                                               //!< execute it instead of continuing.
+       UNLANG_ACTION_BREAK,                    //!< Break out of the current group.
+       UNLANG_ACTION_YIELD,                    //!< Temporarily pause execution until an event occurs.
+       UNLANG_ACTION_STOP_PROCESSING           //!< Break out of processing the current request (unwind).
+} unlang_action_t;
+
+/** Function to call when first evaluating a frame
+ *
+ * @param[in] request          The current request.
+ * @param[in,out] presult      Pointer to the current rcode, may be modified by the function.
+ * @param[in,out] priority     Pointer to the current priority, may be modified by the function.
+ * @return an action for the interpreter to perform.
+ */
+typedef unlang_action_t (*unlang_op_func_t)(REQUEST *request, rlm_rcode_t *presult, int *priority);
+
+/** Function to call if the initial function yielded and the request was signalled
+ *
+ * This is the operation specific cancellation function.  This function will usually
+ * either call a more specialised cancellation function set when something like a module yielded,
+ * or just cleanup the state of the original #unlang_op_func_t.
+ *
+ * @param[in] request          The current request.
+ * @param[in] resume_ctx       A structure allocated by the initial #unlang_op_func_t to store
+ *                             the result of the async execution.
+ * @param[in] action           We're being signalled with.
+ */
+typedef void (*unlang_op_func_signal_t)(REQUEST *request, void *resume_ctx, fr_state_action_t action);
+
+/** Function to call when a request becomes resumable
+ *
+ * When an event occurs that means we can continue processing the request, this function is called
+ * first. This callback is usually used to remove timeout events, unregister interest in file
+ * descriptors, and generally cleanup after the yielding function.
+ *
+ * @param[in] request          The current request.
+ * @param[in] resume_ctx       A structure allocated by the initial #unlang_op_func_t to store
+ *                             the result of the async execution.
+ * @param[in] action           We're being signalled with.
+ */
+typedef void (*unlang_op_func_resumable_t)(REQUEST *request, void *resume_ctx);
+
+/** Function to call if the initial function yielded and the request is resumable
+ *
+ * @param[in] request          The current request.
+ * @param[in,out] presult      Pointer to the current rcode, may be modified by the function.
+ * @param[in] resume_ctx       A structure allocated by the initial #unlang_op_func_t to store
+ *                             the result of the async execution.
+ * @return an action for the interpreter to perform.
+ */
+typedef unlang_action_t (*unlang_op_func_resume_t)(REQUEST *request, rlm_rcode_t *presult, void *resume_ctx);
+
+/** A callback when the the timeout occurs
+ *
+ * Used when a module needs wait for an event.
+ * Typically the callback is set, and then the module returns unlang_module_yield().
+ *
+ * @note The callback is automatically removed on unlang_resumable(), i.e. if an event
+ *     on a registered FD occurs before the timeout event fires.
+ *
+ * @param[in] request          the request.
+ * @param[in] instance         the module instance.
+ * @param[in] thread           data specific to this module instance.
+ * @param[in] rctx             a local context for the callback.
+ * @param[in] fired            the time the timeout event actually fired.
+ */
+typedef        void (*fr_unlang_module_timeout_t)(REQUEST *request, void *instance, void *thread, void *rctx,
+                                          struct timeval *fired);
+
+/** A callback when the FD is ready for reading
+ *
+ * Used when a module needs to read from an FD.  Typically the callback is set, and then the
+ * module returns unlang_module_yield().
+ *
+ * @note The callback is automatically removed on unlang_resumable(), so
+ *
+ * @param[in] request          the current request.
+ * @param[in] instance         the module instance.
+ * @param[in] thread           data specific to this module instance.
+ * @param[in] rctx             a local context for the callback.
+ * @param[in] fd               the file descriptor.
+ */
+typedef void (*fr_unlang_module_fd_event_t)(REQUEST *request, void *instance, void *thread, void *rctx, int fd);
+
+/** A callback for when the request is resumed.
+ *
+ * The resumed request cannot call the normal "authorize", etc. method.  It needs a separate callback.
+ *
+ * @param[in] request          the current request.
+ * @param[in] instance         The module instance.
+ * @param[in] thread           data specific to this module instance.
+ * @param[in] rctx             a local context for the callback.
+ * @return a normal rlm_rcode_t.
+ */
+typedef rlm_rcode_t (*fr_unlang_module_resume_t)(REQUEST *request, void *instance, void *thread, void *rctx);
+
+/** A callback when the request gets a fr_state_action_t.
+ *
+ * A module may call unlang_yeild(), but still need to do something on FR_ACTION_DUP.  If so, it's
+ * set here.
+ *
+ * @note The callback is automatically removed on unlang_resumable().
+ *
+ * @param[in] request          The current request.
+ * @param[in] instance         The module instance.
+ * @param[in] thread           data specific to this module instance.
+ * @param[in] rctx             Resume ctx for the callback.
+ * @param[in] action           which is signalling the request.
+ */
+typedef void (*fr_unlang_module_signal_t)(REQUEST *request, void *instance, void *thread,
+                                         void *rctx, fr_state_action_t action);
+
+/** An unlang operation
+ *
+ * These are like the opcodes in other interpreters.  Each operation, when executed
+ * will return an #unlang_action_t, which determines what the interpreter does next.
+ */
+typedef struct {
+       char const              *name;                          //!< Name of the operation.
+       unlang_op_func_t        func;                           //!< Called when we start the operation.
+
+       unlang_op_func_signal_t signal;                         //!< Called if the request is to be destroyed
+                                                               ///< and we need to cleanup any residual state.
+
+       unlang_op_func_resumable_t resumable;                   //!< Called as soon as the interpreter is informed
+                                                               ///< that a request is resumable.
+
+       unlang_op_func_resume_t resume;                         //!< Called if we're continuing processing
+                                                               ///< a request.
+       bool                    debug_braces;                   //!< Whether the operation needs to print braces
+                                                               ///< in debug mode.
+} unlang_op_t;
+
+void           unlang_push_section(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
+
+rlm_rcode_t    unlang_push_module_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
+                                       REQUEST *request, xlat_exp_t const *xlat,
+                                       fr_unlang_module_resume_t callback,
+                                       fr_unlang_module_signal_t signal_callback, void *uctx);
+
+rlm_rcode_t    unlang_interpret_continue(REQUEST *request);
+
+rlm_rcode_t    unlang_interpret(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
+
+rlm_rcode_t    unlang_interpret_synchronous(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t action);
+
+void           *unlang_stack_alloc(TALLOC_CTX *ctx);
+
+void           unlang_op_register(int type, unlang_op_t *op);
+
+int            unlang_compile(CONF_SECTION *cs, rlm_components_t component);
+
+int            unlang_compile_subsection(CONF_SECTION *server_cs, char const *name1, char const *name2, rlm_components_t component);
+
+bool           unlang_keyword(const char *name);
+
+int            unlang_event_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
+                                        void const *ctx, struct timeval *timeout);
+
+int            unlang_event_fd_add(REQUEST *request,
+                                   fr_unlang_module_fd_event_t read,
+                                   fr_unlang_module_fd_event_t write,
+                                   fr_unlang_module_fd_event_t error,
+                                   void const *ctx, int fd);
+
+int            unlang_event_timeout_delete(REQUEST *request, void const *ctx);
+
+int            unlang_event_fd_delete(REQUEST *request, void const *ctx, int fd);
+
+void           unlang_resumable(REQUEST *request);
+
+void           unlang_signal(REQUEST *request, fr_state_action_t action);
+
+int            unlang_stack_depth(REQUEST *request);
+
+rlm_rcode_t    unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
+                                   fr_unlang_module_signal_t signal_callback, void *ctx);
+xlat_action_t  unlang_xlat_yield(REQUEST *request, xlat_resume_callback_t callback,
+                                 fr_unlang_module_signal_t signal_callback, void *rctx);
+
+int            unlang_initialize(void);
+#endif /* _FR_UNLANG_H */
index 8036c920c172776f34b5b8c0b2d146bacb8d0a5f..d249ba4eb332c5f6cc8de479825bdbd6fb3ddffe 100644 (file)
@@ -30,8 +30,8 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
 #include <freeradius-devel/parser.h>
+#include <freeradius-devel/unlang.h>
 
 fr_thread_local_setup(rbtree_t *, module_thread_inst_tree)
 
@@ -39,19 +39,6 @@ static TALLOC_CTX *instance_ctx = NULL;
 
 static int module_instantiate(CONF_SECTION *root, char const *name);
 
-static bool is_reserved_word(const char *name)
-{
-       int i;
-
-       if (!name || !*name) return false;
-
-       for (i = 1; unlang_ops[i].name != NULL; i++) {
-               if (strcmp(name, unlang_ops[i].name) == 0) return true;
-       }
-
-       return false;
-}
-
 /** Initialise a module specific exfile handle
  *
  * @see exfile_init
@@ -803,7 +790,6 @@ static int _module_instance_free(module_instance_t *mod_inst)
  */
 static module_instance_t *module_bootstrap(CONF_SECTION *modules, CONF_SECTION *cs)
 {
-       int                     i;
        char const              *name1, *inst_name;
        module_instance_t       *mod_inst;
 
@@ -814,15 +800,9 @@ static module_instance_t *module_bootstrap(CONF_SECTION *modules, CONF_SECTION *
        inst_name = cf_section_name2(cs);
        if (!inst_name) inst_name = name1;
 
-       /*
-        *      Don't allow modules to use reserved words.
-        */
-       for (i = 1; unlang_ops[i].name != NULL; i++) {
-               if (strcmp(inst_name, unlang_ops[i].name) == 0) {
-                       ERROR("Module names cannot use a reserved word \"%s\"",
-                             unlang_ops[i].name);
-                       return NULL;
-               }
+       if (unlang_keyword(inst_name)) {
+               ERROR("Module names cannot use a reserved word \"%s\"", inst_name);
+               return NULL;
        }
 
        /*
@@ -906,7 +886,7 @@ static int virtual_module_bootstrap(CONF_SECTION *modules, CONF_SECTION *vm_cs)
                        return -1;
                }
 
-               if (is_reserved_word(name)) {
+               if (unlang_keyword(name)) {
                is_reserved:
                        cf_log_err(vm_cs, "Virtual modules cannot overload unlang keywords");
                        return -1;
@@ -1016,7 +996,7 @@ int modules_bootstrap(CONF_SECTION *root)
 
                name1 = cf_section_name1(subcs);
 
-               if (is_reserved_word(name1)) {
+               if (unlang_keyword(name1)) {
                        cf_log_err(subcs, "Modules cannot overload unlang keywords");
                        return -1;
                }
index 1c20ed0d6aa6217463d9f9ec494ffdbd1c2d06e6..e4e5d024603cce3d1c5144811b646c90977294af 100644 (file)
@@ -25,8 +25,8 @@
 RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/interpreter.h>
 #include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/unlang.h>
 
 /** Per-request opaque data, added by modules
  *
@@ -105,25 +105,8 @@ REQUEST *request_alloc(TALLOC_CTX *ctx)
        request->module = NULL;
        request->component = "<core>";
 
-#ifdef HAVE_TALLOC_POOLED_OBJECT
-       /*
-        *      If we have talloc_pooled_object allocate the
-        *      stack as a combined chunk/pool, with memory
-        *      to hold at mutable data for at least a quarter
-        *      of the maximum number of stack frames.
-        *
-        *      Having a dedicated pool for mutable stack data
-        *      means we don't have memory fragmentations issues
-        *      as we would if request were used as the pool.
-        *
-        *      This number is pretty arbitrary, but it seems
-        *      like too low level to make into a tuneable.
-        */
-       request->stack = talloc_pooled_object(request, unlang_stack_t, UNLANG_STACK_MAX / 4,
-                                             sizeof(unlang_stack_state_t));
-#else
-       request->stack = talloc_zero(request, unlang_stack_t);
-#endif
+       MEM(request->stack = unlang_stack_alloc(request));
+
        request->runnable_id = -1;
        request->time_order_id = -1;
 
index fb77d9c647455cc29cfdeacb8ebf53a71368642b..280b85daa1f22ef9cd78c1a3330af718a13ce94e 100644 (file)
@@ -26,8 +26,9 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
 #include <freeradius-devel/parser.h>
+#include <freeradius-devel/unlang.h>
+#include "unlang_priv.h"
 
 /* Here's where we recognize all of our keywords: first the rcodes, then the
  * actions */
@@ -3239,3 +3240,23 @@ int unlang_compile_subsection(CONF_SECTION *server_cs, char const *name1, char c
 
        return 1;
 }
+
+/** Check if name is an unlang keyword
+ *
+ * @param[in] name     to check.
+ * @return
+ *     - true if it is a keyword.
+ *     - false if it's not a keyword.
+ */
+bool unlang_keyword(const char *name)
+{
+       int i;
+
+       if (!name || !*name) return false;
+
+       for (i = 1; compile_table[i].name != NULL; i++) {
+               if (strcmp(name, compile_table[i].name) == 0) return true;
+       }
+
+       return false;
+}
index 013b545696eaabe3d7c4f9008f7c6b3c11e78825..4688862f31a6da917905d787a83e918e3dee216f 100644 (file)
@@ -26,11 +26,12 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
 #include <freeradius-devel/parser.h>
 #include <freeradius-devel/xlat.h>
 #include <freeradius-devel/io/listen.h>
 
+#include "unlang_priv.h"
+
 static FR_NAME_NUMBER unlang_action_table[] = {
        { "calculate-result",   UNLANG_ACTION_CALCULATE_RESULT },
        { "continue",           UNLANG_ACTION_CONTINUE },
@@ -95,6 +96,10 @@ static void unlang_dump_stack(REQUEST *request)
 #define DUMP_STACK
 #endif
 
+/** Different operations the interpreter can execute
+ */
+unlang_op_t unlang_ops[UNLANG_TYPE_MAX];
+
 /** Allocates and initializes an unlang_resume_t
  *
  * @param[in] request          The current request.
@@ -790,6 +795,35 @@ rlm_rcode_t unlang_interpret_synchronous(REQUEST *request, CONF_SECTION *cs, rlm
        return rcode;
 }
 
+/** Allocate a new unlang stack
+ *
+ * @param[in] ctx      to allocate stack in.
+ * @return
+ *     - A new stack on success.
+ *     - NULL on OOM.
+ */
+void *unlang_stack_alloc(TALLOC_CTX *ctx)
+{
+#ifdef HAVE_TALLOC_POOLED_OBJECT
+       /*
+        *      If we have talloc_pooled_object allocate the
+        *      stack as a combined chunk/pool, with memory
+        *      to hold at mutable data for at least a quarter
+        *      of the maximum number of stack frames.
+        *
+        *      Having a dedicated pool for mutable stack data
+        *      means we don't have memory fragmentations issues
+        *      as we would if request were used as the pool.
+        *
+        *      This number is pretty arbitrary, but it seems
+        *      like too low level to make into a tuneable.
+        */
+       return talloc_pooled_object(ctx, unlang_stack_t, UNLANG_STACK_MAX / 4, sizeof(unlang_stack_state_t));
+#else
+       return talloc_zero(ctx, unlang_stack_t);
+#endif
+}
+
 /** Wrap an #fr_event_timer_t providing data needed for unlang events
  *
  */
@@ -1257,6 +1291,66 @@ void unlang_resumable(REQUEST *request)
        if (request->runnable_id < 0) fr_heap_insert(request->backlog, request);
 }
 
+/** Callback for handling resumption frames
+ *
+ * Resumption frames are added to track when a module, or other construct
+ * has yielded control back to the interpreter.
+ *
+ * This function is called when the request has been marked as resumable
+ * and a resumption frame was previously placed on the stack, i.e. when
+ * the work that caused the request to be yielded initially has completed.
+ *
+ * @param[in] request  to be resumed.
+ * @param[out] presult the rcode returned by the resume function.
+ * @param[out] priority associated with the rcode.
+ */
+static unlang_action_t unlang_resume(REQUEST *request, rlm_rcode_t *presult, int *priority)
+{
+       unlang_stack_t                  *stack = request->stack;
+       unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
+       unlang_t                        *instruction = frame->instruction;
+       unlang_resume_t                 *mr = unlang_generic_to_resume(instruction);
+       unlang_action_t                 action;
+
+       RDEBUG3("Resuming in %s", mr->self.debug_name);
+
+       if (!unlang_ops[mr->parent->type].resume) {
+               *presult = RLM_MODULE_FAIL;
+               return UNLANG_ACTION_CALCULATE_RESULT;
+       }
+
+       request->module = mr->self.debug_name;
+
+       /*
+        *      Run the resume callback associated with
+        *      the original frame which was used to
+        *      create this resumption frame.
+        */
+       action = unlang_ops[mr->parent->type].resume(request, presult, mr->resume_ctx);
+
+       request->module = NULL;
+
+       /*
+        *      Leave mr alone, it will be freed when the request is done.
+        */
+
+       /*
+        *      Is now marked as "stop" when it wasn't before, we must have been blocked.
+        */
+       if (request->master_state == REQUEST_STOP_PROCESSING) {
+               RWARN("Module %s became unblocked", mr->self.debug_name);
+               return UNLANG_ACTION_STOP_PROCESSING;
+       }
+
+       if (*presult != RLM_MODULE_YIELD) {
+               rad_assert(*presult >= RLM_MODULE_REJECT);
+               rad_assert(*presult < RLM_MODULE_NUMCODES);
+               *priority = instruction->actions[*presult];
+       }
+
+       return action;
+}
+
 /** Yield a request back to the interpreter from within a module
  *
  * This passes control of the request back to the unlang interpreter, setting
@@ -1461,6 +1555,30 @@ static ssize_t xlat_interpreter(UNUSED TALLOC_CTX *ctx, char **out, size_t outle
        return 0;
 }
 
+/** Register an operation with the interpreter
+ *
+ * The main purpose of this registration API is to avoid intermixing the xlat,
+ * condition, map APIs with the interpreter, i.e. the callbacks needed for that
+ * functionality can be in their own source files, and we don't need to include
+ * supporting types and function declarations in the interpreter.
+ *
+ * Later, this could potentially be used to register custom operations for modules.
+ *
+ * The reason why there's a function instead of accessing the unlang_op array
+ * directly, is because 'type' really needs to go away, as needing to add ops to
+ * the unlang_type_t enum breaks the pluggable module model. If there's no
+ * explicit/consistent type values we need to enumerate the operations ourselves.
+ *
+ * @param[in] type             Operation identifier.  Used to map compiled unlang code
+ *                             to operations.
+ * @param[in] op               unlang_op to register.
+ */
+void unlang_op_register(int type, unlang_op_t *op)
+{
+       rad_assert(type < UNLANG_TYPE_MAX);     /* Unlang max isn't a valid type */
+
+       memcpy(&unlang_ops[type], op, sizeof(unlang_ops[type]));
+}
 
 /** Initialize the unlang compiler / interpreter.
  *
@@ -1470,5 +1588,8 @@ int unlang_initialize(void)
 {
        (void) xlat_register(NULL, "interpreter", xlat_interpreter, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
 
+       unlang_op_register(UNLANG_TYPE_RESUME, &(unlang_op_t){ .name = "resume", .func = unlang_resume });
+       unlang_op_initialize(); /* Register operations for the default keywords */
+
        return 0;
 }
index aa322c1a70981fc20f59020110f3df9c5696ff6f..aa3ed493f2f8d4f893ff6ebaa645bbaae59fea5d 100644 (file)
@@ -26,11 +26,12 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
 #include <freeradius-devel/parser.h>
 #include <freeradius-devel/xlat.h>
 #include <freeradius-devel/io/listen.h>
 
+#include "unlang_priv.h"
+
 /*
  *     Some functions differ mainly in their parsing
  */
@@ -1489,7 +1490,7 @@ static unlang_action_t unlang_foreach(REQUEST *request,
 
                /*
                 *      Copy the VPs from the original request, this ensures deterministic
-                *      behaviour if someone decides to add or remove VPs in the set were
+                *      behaviour if someone decides to add or remove VPs in the set we're
                 *      iterating over.
                 */
                if (tmpl_copy_vps(request, &vps, request, g->vpt) < 0) {        /* nothing to loop over */
@@ -1984,186 +1985,158 @@ static unlang_action_t unlang_if(REQUEST *request,
 }
 
 
-/** Callback for handling resumption frames
- *
- * Resumption frames are added to track when a module, or other construct
- * has yielded control back to the interpreter.
- *
- * This function is called when the request has been marked as resumable
- * and a resumption frame was previously placed on the stack, i.e. when
- * the work that caused the request to be yielded initially has completed.
- *
- * @param[in] request  to be resumed.
- * @param[out] presult the rcode returned by the resume function.
- * @param[out] priority associated with the rcode.
- */
-static unlang_action_t unlang_resume(REQUEST *request, rlm_rcode_t *presult, int *priority)
+void unlang_op_initialize(void)
 {
-       unlang_stack_t                  *stack = request->stack;
-       unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
-       unlang_t                        *instruction = frame->instruction;
-       unlang_resume_t                 *mr = unlang_generic_to_resume(instruction);
-       unlang_action_t                 action;
-
-       RDEBUG3("Resuming in %s", mr->self.debug_name);
-
-       if (!unlang_ops[mr->parent->type].resume) {
-               *presult = RLM_MODULE_FAIL;
-               return UNLANG_ACTION_CALCULATE_RESULT;
-       }
-
-       request->module = mr->self.debug_name;
-
-       /*
-        *      Run the resume callback associated with
-        *      the original frame which was used to
-        *      create this resumption frame.
-        */
-       action = unlang_ops[mr->parent->type].resume(request, presult, mr->resume_ctx);
-
-       request->module = NULL;
-
-       /*
-        *      Leave mr alone, it will be freed when the request is done.
-        */
-
-       /*
-        *      Is now marked as "stop" when it wasn't before, we must have been blocked.
-        */
-       if (request->master_state == REQUEST_STOP_PROCESSING) {
-               RWARN("Module %s became unblocked", mr->self.debug_name);
-               return UNLANG_ACTION_STOP_PROCESSING;
-       }
+       unlang_op_register(UNLANG_TYPE_MODULE_CALL,
+                          &(unlang_op_t){
+                               .name = "module",
+                               .func = unlang_module_call,
+                               .signal = unlang_module_signal,
+                               .resume = unlang_module_resume
+                          });
+
+       unlang_op_register(UNLANG_TYPE_GROUP,
+                          &(unlang_op_t){
+                               .name = "group",
+                               .func = unlang_group,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_LOAD_BALANCE,
+                          &(unlang_op_t){
+                               .name = "load-balance group",
+                               .func = unlang_load_balance,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_REDUNDANT_LOAD_BALANCE,
+                          &(unlang_op_t){
+                               .name = "redundant-load-balance group",
+                               .func = unlang_redundant_load_balance,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_PARALLEL,
+                          &(unlang_op_t){
+                               .name = "parallel",
+                               .func = unlang_parallel,
+                               .signal = unlang_parallel_signal,
+                               .resumable = unlang_parallel_resumable,
+                               .resume = unlang_parallel_resume,
+                               .debug_braces = true
+                          });
 
-       if (*presult != RLM_MODULE_YIELD) {
-               rad_assert(*presult >= RLM_MODULE_REJECT);
-               rad_assert(*presult < RLM_MODULE_NUMCODES);
-               *priority = instruction->actions[*presult];
-       }
-
-       return action;
-}
-
-unlang_op_t unlang_ops[] = {
-       [UNLANG_TYPE_MODULE_CALL] = {
-               .name = "module-call",
-               .func = unlang_module_call,
-               .signal = unlang_module_signal,
-               .resume = unlang_module_resume,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_GROUP] = {
-               .name = "group",
-               .func = unlang_group,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_LOAD_BALANCE] = {
-               .name = "load-balance group",
-               .func = unlang_load_balance,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_REDUNDANT_LOAD_BALANCE] = {
-               .name = "redundant-load-balance group",
-               .func = unlang_redundant_load_balance,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_PARALLEL] = {
-               .name = "parallel",
-               .func = unlang_parallel,
-               .signal = unlang_parallel_signal,
-               .resumable = unlang_parallel_resumable,
-               .resume = unlang_parallel_resume,
-               .debug_braces = true
-       },
 #ifdef WITH_UNLANG
-       [UNLANG_TYPE_IF] = {
-               .name = "if",
-               .func = unlang_if,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_ELSE] = {
-               .name = "else",
-               .func = unlang_group,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_ELSIF] = {
-               .name = "elsif",
-               .func = unlang_if,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_UPDATE] = {
-               .name = "update",
-               .func = unlang_update,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_SWITCH] = {
-               .name = "switch",
-               .func = unlang_switch,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_CASE] = {
-               .name = "case",
-               .func = unlang_case,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_FOREACH] = {
-               .name = "foreach",
-               .func = unlang_foreach,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_BREAK] = {
-               .name = "break",
-               .func = unlang_break,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_RETURN] = {
-               .name = "return",
-               .func = unlang_return,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_MAP] = {
-               .name = "map",
-               .func = unlang_map,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_POLICY] = {
-               .name = "policy",
-               .func = unlang_policy,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_SUBREQUEST] = {
-               .name = "subrequest",
-               .func = unlang_subrequest,
-               .signal = unlang_subrequest_signal,
-               .resume = unlang_subrequest_resume,
-               .debug_braces = true
-       },
-       [UNLANG_TYPE_DETACH] = {
-               .name = "detach",
-               .func = unlang_detach,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_CALL] = {
-               .name = "call",
-               .func = unlang_call,
-               .debug_braces = true
-       },
+       unlang_op_register(UNLANG_TYPE_IF,
+                          &(unlang_op_t){
+                               .name = "if",
+                               .func = unlang_if,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_ELSE,
+                          &(unlang_op_t){
+                               .name = "else",
+                               .func = unlang_group,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_ELSIF,
+                          &(unlang_op_t){
+                               .name = "elseif",
+                               .func = unlang_if,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_UPDATE,
+                          &(unlang_op_t){
+                               .name = "update",
+                               .func = unlang_update,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_SWITCH,
+                          &(unlang_op_t){
+                               .name = "switch",
+                               .func = unlang_switch,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_CASE,
+                          &(unlang_op_t){
+                               .name = "case",
+                               .func = unlang_case,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_FOREACH,
+                          &(unlang_op_t){
+                               .name = "foreach",
+                               .func = unlang_foreach,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_BREAK,
+                          &(unlang_op_t){
+                               .name = "break",
+                               .func = unlang_break,
+                          });
+
+       unlang_op_register(UNLANG_TYPE_RETURN,
+                          &(unlang_op_t){
+                               .name = "return",
+                               .func = unlang_return,
+                          });
+
+       unlang_op_register(UNLANG_TYPE_MAP,
+                          &(unlang_op_t){
+                               .name = "map",
+                               .func = unlang_map,
+                          });
+
+
+       unlang_op_register(UNLANG_TYPE_POLICY,
+                          &(unlang_op_t){
+                               .name = "policy",
+                               .func = unlang_policy,
+                          });
+
+
+       unlang_op_register(UNLANG_TYPE_SUBREQUEST,
+                          &(unlang_op_t){
+                               .name = "subrequest",
+                               .func = unlang_subrequest,
+                               .signal = unlang_subrequest_signal,
+                               .resume = unlang_subrequest_resume,
+                               .debug_braces = true
+                          });
+
+       unlang_op_register(UNLANG_TYPE_DETACH,
+                          &(unlang_op_t){
+                               .name = "detach",
+                               .func = unlang_detach,
+                          });
+
+       unlang_op_register(UNLANG_TYPE_CALL,
+                          &(unlang_op_t){
+                               .name = "call",
+                               .func = unlang_call,
+                               .debug_braces = true
+                          });
 #endif
-       [UNLANG_TYPE_XLAT_INLINE] = {
-               .name = "xlat_inline",
-               .func = unlang_xlat_inline,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_XLAT] = {
-               .name = "xlat_eval",
-               .func = unlang_xlat,
-               .resume = unlang_xlat_resume,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_RESUME] = {
-               .name = "resume",
-               .func = unlang_resume,
-               .debug_braces = false
-       },
-       [UNLANG_TYPE_MAX] = { NULL, NULL, NULL, NULL, false }
-};
+
+       unlang_op_register(UNLANG_TYPE_XLAT_INLINE,
+                          &(unlang_op_t){
+                               .name = "xlat_inline",
+                               .func = unlang_xlat_inline,
+                               .debug_braces = false
+                          });
+
+       unlang_op_register(UNLANG_TYPE_XLAT,
+                          &(unlang_op_t){
+                               .name = "xlat_eval",
+                               .func = unlang_xlat,
+                               .resume = unlang_xlat_resume,
+                               .debug_braces = false
+                          });
+}
diff --git a/src/main/unlang_priv.h b/src/main/unlang_priv.h
new file mode 100644 (file)
index 0000000..c411f36
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ *  This program 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _FR_UNLANG_PRIV_H
+#define _FR_UNLANG_PRIV_H
+/**
+ * $Id$
+ *
+ * @file src/main/unlang.h
+ * @brief Private interpreter structures and functions
+ *
+ * @author Alan DeKok <aland@freeradius.org>
+ */
+#include <freeradius-devel/cf_util.h> /* Need CONF_* definitions */
+#include <freeradius-devel/map_proc.h>
+#include <freeradius-devel/modpriv.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/unlang.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UNLANG_STACK_MAX (64)
+
+/* Actions may be a positive integer (the highest one returned in the group
+ * will be returned), or the keyword "return", represented here by
+ * MOD_ACTION_RETURN, to cause an immediate return.
+ * There's also the keyword "reject", represented here by MOD_ACTION_REJECT
+ * to cause an immediate reject. */
+#define MOD_ACTION_RETURN  (-1)
+#define MOD_ACTION_REJECT  (-2)
+#define MOD_PRIORITY_MAX   (64)
+
+/** Types of unlang_t nodes
+ *
+ * Here are our basic types: unlang_t, unlang_group_t, and unlang_module_call_t. For an
+ * explanation of what they are all about, see doc/configurable_failover.rst
+ */
+typedef enum {
+       UNLANG_TYPE_NULL = 0,                   //!< Modcallable type not set.
+       UNLANG_TYPE_MODULE_CALL = 1,            //!< Module method.
+       UNLANG_TYPE_GROUP,                      //!< Grouping section.
+       UNLANG_TYPE_LOAD_BALANCE,               //!< Load balance section.
+       UNLANG_TYPE_REDUNDANT_LOAD_BALANCE,     //!< Redundant load balance section.
+       UNLANG_TYPE_PARALLEL,                   //!< execute statements in parallel
+#ifdef WITH_UNLANG
+       UNLANG_TYPE_IF,                         //!< Condition.
+       UNLANG_TYPE_ELSE,                       //!< !Condition.
+       UNLANG_TYPE_ELSIF,                      //!< !Condition && Condition.
+       UNLANG_TYPE_UPDATE,                     //!< Update block.
+       UNLANG_TYPE_SWITCH,                     //!< Switch section.
+       UNLANG_TYPE_CASE,                       //!< Case section (within a #UNLANG_TYPE_SWITCH).
+       UNLANG_TYPE_FOREACH,                    //!< Foreach section.
+       UNLANG_TYPE_BREAK,                      //!< Break statement (within a #UNLANG_TYPE_FOREACH).
+       UNLANG_TYPE_RETURN,                     //!< Return statement.
+       UNLANG_TYPE_MAP,                        //!< Mapping section (like #UNLANG_TYPE_UPDATE, but uses
+                                               //!< values from a #map_proc_t call).
+       UNLANG_TYPE_SUBREQUEST,                 //!< create a child subrequest
+       UNLANG_TYPE_DETACH,                     //!< detach a child
+       UNLANG_TYPE_CALL,                       //!< call another virtual server
+#endif
+       UNLANG_TYPE_POLICY,                     //!< Policy section.
+       UNLANG_TYPE_XLAT_INLINE,                //!< xlat statement, inline in "unlang"
+       UNLANG_TYPE_XLAT,                       //!< Represents one level of an xlat expansion.
+       UNLANG_TYPE_RESUME,                     //!< where to resume processing
+       UNLANG_TYPE_MAX
+} unlang_type_t;
+
+/** Allows the frame evaluator to signal the interpreter
+ *
+ */
+typedef enum {
+       UNLANG_FRAME_ACTION_POP = 1,            //!< Pop the current frame, and check the next one further
+                                               ///< up in the stack for what to do next.
+       UNLANG_FRAME_ACTION_CONTINUE,           //!< Process the next instruction at this level.
+       UNLANG_FRAME_ACTION_YIELD               //!< Temporarily return control back to the caller on the C
+                                               ///< stack.
+} unlang_frame_action_t;
+
+typedef enum {
+       UNLANG_GROUP_TYPE_SIMPLE = 0,           //!< Execute each of the children sequentially, until we execute
+                                               //!< all of the children, or one returns #UNLANG_ACTION_BREAK.
+       UNLANG_GROUP_TYPE_REDUNDANT,            //!< Execute each of the children until one returns a 'good'
+                                               //!< result i.e. ok, updated, noop, then break out of the group.
+       UNLANG_GROUP_TYPE_MAX                   //!< Number of group types.
+} unlang_group_type_t;
+
+#define UNLANG_NEXT_STOP (false)
+#define UNLANG_NEXT_CONTINUE (true)
+
+#define UNLANG_TOP_FRAME (true)
+#define UNLANG_SUB_FRAME (false)
+
+#define UNLANG_DETACHABLE (true)
+#define UNLANG_NORMAL_CHILD (false)
+
+/** A node in a graph of #unlang_op_t (s) that we execute
+ *
+ * The interpreter acts like a turing machine, with #unlang_t nodes forming the tape
+ * and the #unlang_action_t the instructions.
+ *
+ * This is the parent 'class' for multiple #unlang_t node specialisations.
+ * The #unlang_t struct is listed first in the specialisation so that we can cast between
+ * parent/child classes without knowledge of the layout of the structures.
+ *
+ * The specialisations of the nodes describe additional details of the operation to be performed.
+ */
+typedef struct unlang_t {
+       struct unlang_t         *parent;        //!< Previous node.
+       struct unlang_t         *next;          //!< Next node (executed on #UNLANG_ACTION_CONTINUE et al).
+       char const              *name;          //!< Unknown...
+       char const              *debug_name;    //!< Printed in log messages when the node is executed.
+       unlang_type_t           type;           //!< The specialisation of this node.
+       int                     actions[RLM_MODULE_NUMCODES];   //!< Priorities for the various return codes.
+} unlang_t;
+
+/** Generic representation of a grouping
+ *
+ * Can represent IF statements, maps, update sections etc...
+ */
+typedef struct {
+       unlang_t                self;
+       unlang_group_type_t     group_type;
+       unlang_t                *children;      //!< Children beneath this group.  The body of an if
+                                               //!< section for example.
+       unlang_t                *tail;          //!< of the children list.
+       CONF_SECTION            *cs;
+       int                     num_children;
+
+       /*
+        *      Hackity-hack.  We should probably just have a common
+        *      group header, and then have type-specific structures.
+        */
+       union {
+               struct {
+                       vp_tmpl_t               *vpt;           //!< #UNLANG_TYPE_SWITCH, #UNLANG_TYPE_MAP, #UNLANG_TYPE_CALL
+
+                       union {
+                               struct {
+                                       vp_map_t                *map;           //!< #UNLANG_TYPE_UPDATE, #UNLANG_TYPE_MAP.
+                                       map_proc_inst_t         *proc_inst;     //!< Instantiation data for #UNLANG_TYPE_MAP.
+                               };
+                               struct {
+                                       void const              *process;       //!< #UNLANG_TYPE_CALL
+                                       CONF_SECTION            *server_cs;     //!< #UNLANG_TYPE_CALL
+                               };
+                       };
+               };
+               fr_cond_t               *cond;          //!< #UNLANG_TYPE_IF, #UNLANG_TYPE_ELSIF.
+
+               bool                    clone;          //!< #UNLANG_TYPE_PARALLEL
+       };
+} unlang_group_t;
+
+/** A call to a module method
+ *
+ */
+typedef struct {
+       unlang_t                self;
+       module_instance_t       *module_instance;       //!< Instance of the module we're calling.
+       module_method_t         method;
+} unlang_module_call_t;
+
+/** Pushed onto the interpreter stack by a yielding module, indicates the resumption point
+ *
+ * Unlike normal coroutines in other languages, we represent resumption points as states in a state
+ * machine made up of function pointers.
+ *
+ * When a module yields, it specifies the function to call when whatever condition is
+ * required for resumption is satisfied, it also specifies the ctx for that function,
+ * which represents the internal state of the module at the time of yielding.
+ *
+ * If you want normal coroutine behaviour... ctx is arbitrary and could include a state enum,
+ * in which case the function pointer could be the same as the function that yielded, and something
+ * like Duff's device could be used to jump back to the yield point.
+ *
+ * Yield/resume are left as flexible as possible.  Writing async code this way is difficult enough
+ * without being straightjacketed.
+ */
+typedef struct {
+       unlang_t                self;
+       unlang_t                *parent;                        //!< The original instruction.
+
+       void                    *callback;                      //!< Function the yielding code indicated should
+                                                               //!< be called when the request could be resumed.
+
+       void                    *signal;                        //!< Function the yielding code indicated should
+                                                               ///< be called if the request is destroyed in
+                                                               ///< the middle of an async operation.
+
+       void                    *resume_ctx;                    //!< Context data for the callback.  Usually represents
+                                                               ///< the function's internal state at the time of
+                                                               ///< yielding.
+} unlang_resume_t;
+
+/** A naked xlat
+ *
+ * @note These are vestigial and may be removed in future.
+ */
+typedef struct {
+       unlang_t                self;
+       int                     exec;
+       char                    *xlat_name;
+       xlat_exp_t              *exp;                           //!< First xlat node to execute.
+} unlang_xlat_inline_t;
+
+/** A module stack entry
+ *
+ * Represents a single module call.
+ */
+typedef struct {
+       module_thread_instance_t *thread;                       //!< thread-local data for this module
+} unlang_stack_state_modcall_t;
+
+/** State of a foreach loop
+ *
+ */
+typedef struct {
+       vp_cursor_t             cursor;                         //!< Used to track our place in the list
+                                                               ///< we're iterating over.
+       VALUE_PAIR              *vps;                           //!< List containing the attribute(s) we're
+                                                               ///< iterating over.
+       VALUE_PAIR              *variable;                      //!< Attribute we update the value of.
+       int                     depth;                          //!< Level of nesting of this foreach loop.
+#ifndef NDEBUG
+       int                     indent;                         //!< for catching indentation issues
+#endif
+} unlang_stack_state_foreach_t;
+
+/** State of a redundant operation
+ *
+ */
+typedef struct {
+       unlang_t                *child;
+       unlang_t                *found;
+} unlang_stack_state_redundant_t;
+
+/** Hold the result of an inline xlat expansion
+ *
+ */
+typedef struct {
+       fr_value_box_t          *result;                        //!< Where to store the result of the
+                                                               ///< xlat expansion. This is usually discarded.
+} unlang_stack_state_xlat_inline_t;
+
+/** State of an xlat expansion
+ *
+ * State of one level of nesting within an xlat expansion.
+ */
+typedef struct {
+       TALLOC_CTX              *ctx;                           //!< to allocate boxes and values in.
+       xlat_exp_t const        *exp;
+       fr_cursor_t             values;                         //!< Values aggregated so far.
+
+       /*
+        *      For func and alternate
+        */
+       fr_value_box_t          *rhead;                         //!< Head of the result of a nested
+                                                               ///< expansion.
+       fr_cursor_t             result;                         //!< Result cursor, mainly useful for
+                                                               ///< asynchronous xlat functions.
+       bool                    alternate;                      //!< record which alternate branch we
+                                                               ///< previously took.
+} unlang_stack_state_xlat_t;
+
+/** Our interpreter stack, as distinct from the C stack
+ *
+ * We don't call the modules recursively.  Instead we iterate over a list of #unlang_t and
+ * and manage the call stack ourselves.
+ *
+ * After looking at various green thread implementations, it was decided that using the existing
+ * unlang interpreter stack was the best way to perform async I/O.
+ *
+ * Each request as an unlang interpreter stack associated with it, which represents its progress
+ * through the server.  Because the interpreter stack is distinct from the C stack, we can have
+ * a single system thread with many thousands of pending requests.
+ */
+typedef struct {
+       unlang_t                *instruction;                   //!< The unlang node we're evaluating.
+       unlang_t                *next;                          //!< The next unlang node we will evaluate
+
+       /** Stack frame specialisations
+        *
+        * These store extra (mutable) state data, for the immutable (#unlang_t)
+        * instruction.  Instructions can't be used to store data because they
+        * might be shared between multiple threads.
+        *
+        * Which stack_entry specialisation to use is determined by the
+        * instruction->type.
+        */
+       void                    *state;
+
+       rlm_rcode_t             result;                         //!< The result from executing the instruction.
+       int                     priority;                       //!< Result priority.  When we pop this stack frame
+                                                               ///< this priority will be compared with the one of the
+                                                               ///< frame lower in the stack to determine if the
+                                                               ///< result stored in the lower stack frame should
+                                                               ///< be replaced.
+
+       unlang_type_t           unwind;                         //!< Unwind to this one if it exists.
+                                                               ///< This is used for break and return.
+
+       bool                    repeat : 1;                     //!< Call the action callback again on our way
+                                                               //!< back up the stack.
+       bool                    top_frame : 1;                  //!< are we the top frame of the stack?
+
+       union {
+               unlang_stack_state_foreach_t    foreach;        //!< Foreach iterator state.
+               unlang_stack_state_redundant_t  redundant;      //!< Redundant section state.
+       };
+} unlang_stack_frame_t;
+
+/** An unlang stack associated with a request
+ *
+ */
+typedef struct {
+       int                     depth;                          //!< Current depth we're executing at.
+       unlang_stack_frame_t    frame[UNLANG_STACK_MAX];        //!< The stack...
+} unlang_stack_t;
+
+/** Different operations the interpreter can execute
+ */
+extern unlang_op_t unlang_ops[];
+
+#define MOD_NUM_TYPES (UNLANG_TYPE_XLAT + 1)
+
+extern char const *const comp2str[];
+
+/** @name Conversion functions for converting #unlang_t to its specialisations
+ *
+ * Simple conversions: #unlang_module_call_t and #unlang_group_t are subclasses of #unlang_t,
+ * so we often want to go back and forth between them.
+ *
+ * @{
+ */
+static inline unlang_module_call_t *unlang_generic_to_module_call(unlang_t *p)
+{
+       rad_assert(p->type == UNLANG_TYPE_MODULE_CALL);
+       return talloc_get_type_abort(p, unlang_module_call_t);
+}
+
+static inline unlang_group_t *unlang_generic_to_group(unlang_t *p)
+{
+       rad_assert((p->type > UNLANG_TYPE_MODULE_CALL) && (p->type <= UNLANG_TYPE_POLICY));
+
+       return (unlang_group_t *)p;
+}
+
+static inline unlang_t *unlang_module_call_to_generic(unlang_module_call_t *p)
+{
+       return (unlang_t *)p;
+}
+
+static inline unlang_t *unlang_group_to_generic(unlang_group_t *p)
+{
+       return (unlang_t *)p;
+}
+
+static inline unlang_xlat_inline_t *unlang_generic_to_xlat_inline(unlang_t *p)
+{
+       rad_assert(p->type == UNLANG_TYPE_XLAT_INLINE);
+       return talloc_get_type_abort(p, unlang_xlat_inline_t);
+}
+
+static inline unlang_t *unlang_xlat_inline_to_generic(unlang_xlat_inline_t *p)
+{
+       return (unlang_t *)p;
+}
+
+static inline unlang_resume_t *unlang_generic_to_resume(unlang_t *p)
+{
+       rad_assert(p->type == UNLANG_TYPE_RESUME);
+       return talloc_get_type_abort(p, unlang_resume_t);
+}
+
+static inline unlang_t *unlang_resume_to_generic(unlang_resume_t *p)
+{
+       return (unlang_t *)p;
+}
+/* @} **/
+
+/*
+ *     Internal interpreter functions needed by ops
+ */
+void           unlang_push(unlang_stack_t *stack, unlang_t *program, rlm_rcode_t result,
+                           bool do_next_sibling, bool top_frame);
+rlm_rcode_t    unlang_run(REQUEST *request);
+
+void           unlang_op_initialize(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 391d12779a5b355ee0cf688306523321213c24c1..829b0b687f695279f06c1632b624f79fa3330cfd 100644 (file)
@@ -29,7 +29,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
+#include <freeradius-devel/unlang.h>
 #include <freeradius-devel/parser.h>
 #include <freeradius-devel/protocol.h>
 #include <freeradius-devel/io/listen.h>