It lets us return an unlang_action_t from push functions, which results in less boilerplate.
*/
static inline CC_HINT(always_inline) unlang_action_t eap_tls_handshake_push(request_t *request, eap_session_t *eap_session)
{
- if (unlang_interpret_push_function(request,
- eap_tls_handshake,
- eap_tls_handshake_resume,
- NULL, UNLANG_SUB_FRAME, eap_session) < 0) return UNLANG_ACTION_STOP_PROCESSING;
+ if (unlang_function_push(request,
+ eap_tls_handshake,
+ eap_tls_handshake_resume,
+ NULL, UNLANG_SUB_FRAME, eap_session) < 0) return UNLANG_ACTION_STOP_PROCESSING;
return UNLANG_ACTION_PUSHED_CHILD;
}
}
}
- if (unlang_interpret_push_function(child, trigger_run, trigger_resume,
+ if (unlang_function_push(child, trigger_run, trigger_resume,
NULL, UNLANG_TOP_FRAME, trigger) < 0) goto error;
if (!intp) {
* These deal exclusively with control flow.
*/
typedef enum {
+ UNLANG_ACTION_FAIL = -1, //!< Encountered an unexpected error.
UNLANG_ACTION_CALCULATE_RESULT = 1, //!< Calculate a new section #rlm_rcode_t value.
UNLANG_ACTION_EXECUTE_NEXT, //!< Execute the next #unlang_t.
UNLANG_ACTION_PUSHED_CHILD, //!< #unlang_t pushed a new child onto the stack,
unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
char const *caller;
- *p_result = RLM_MODULE_NOOP; /* Avoid asserts */
-
/*
* Don't let the callback mess with the current
* module permanently.
unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
char const *caller;
- *p_result = RLM_MODULE_NOOP; /* Avoid asserts */
-
/*
* Don't let the callback mess with the current
* module permanently.
return ua;
}
+/** Clear pending repeat function calls, and remove the signal handler.
+ *
+ * The function frame being modified must be at the top of the stack.
+ *
+ * @param[in] request The current request.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int unlang_function_clear(request_t *request)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_frame_state_func_t *state;
+
+ if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
+ RERROR("Can't clear function on non-function frame");
+ return -1;
+ }
+
+ state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
+ state->repeat = NULL;
+ state->signal = NULL;
+
+ repeatable_clear(frame);
+
+ return 0;
+}
+
+/** Set a new repeat function for an existing function frame
+ *
+ * The function frame being modified must be at the top of the stack.
+ *
+ * @param[in] request The current request.
+ * @param[in] repeat the repeat function to set.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int unlang_function_repeat(request_t *request, unlang_function_t repeat)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_frame_state_func_t *state;
+
+ if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
+ RERROR("Can't set repeat function on non-function frame");
+ return -1;
+ }
+
+ state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
+
+ /*
+ * If we're inside unlang_function_call,
+ * it'll pickup state->repeat and do the right thing
+ * once the current function returns.
+ */
+ state->repeat = repeat;
+
+ return 0;
+}
+
/** Push a generic function onto the unlang stack
*
* These can be pushed by any other type of unlang op to allow a submodule or function
* - 0 on success.
* - -1 on failure.
*/
-int unlang_interpret_push_function(request_t *request, unlang_function_t func, unlang_function_t repeat,
- unlang_function_signal_t signal, bool top_frame, void *uctx)
+unlang_action_t unlang_function_push(request_t *request, unlang_function_t func, unlang_function_t repeat,
+ unlang_function_signal_t signal, bool top_frame, void *uctx)
{
unlang_stack_t *stack = request->stack;
unlang_stack_frame_t *frame;
* Push module's function
*/
if (unlang_interpret_push(request, &function_instruction,
- RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, top_frame) < 0) return -1;
+ RLM_MODULE_NOOP, UNLANG_NEXT_STOP, top_frame) < 0) return UNLANG_ACTION_FAIL;
frame = &stack->frame[stack->depth];
*/
if (!func && repeat) frame->process = unlang_function_call_repeat;
- return 0;
+ return UNLANG_ACTION_PUSHED_CHILD;
}
void unlang_function_init(void)
* @file unlang/function.h
* @brief Declarations for generic unlang functions.
*
+ * These are a useful alternative to module methods for library code.
+ * They're more light weight, and don't require instance data lookups
+ * to function.
+ *
* @copyright 2021 The FreeRADIUS server project
*/
*/
typedef void (*unlang_function_signal_t)(request_t *request, fr_state_signal_t action, void *uctx);
-int unlang_interpret_push_function(request_t *request,
- unlang_function_t func, unlang_function_t repeat,
- unlang_function_signal_t signal, bool top_frame, void *uctx)
- CC_HINT(warn_unused_result);
+int unlang_function_clear(request_t *request) CC_HINT(warn_unused_result);
+
+int unlang_function_repeat(request_t *request, unlang_function_t repeat) CC_HINT(warn_unused_result);
+
+unlang_action_t unlang_function_push(request_t *request,
+ unlang_function_t func, unlang_function_t repeat,
+ unlang_function_signal_t signal, bool top_frame, void *uctx)
+ CC_HINT(warn_unused_result);
#ifdef __cplusplus
}
*/
while (frame->instruction) {
unlang_t const *instruction = frame->instruction;
- unlang_action_t action = UNLANG_ACTION_UNWIND;
+ unlang_action_t ua = UNLANG_ACTION_UNWIND;
DUMP_STACK;
* should be evaluated again.
*/
repeatable_clear(frame);
- action = frame->process(result, request, frame);
+ ua = frame->process(result, request, frame);
RDEBUG4("** [%i] %s << %s (%d)", stack->depth, __FUNCTION__,
- fr_table_str_by_value(unlang_action_table, action, "<INVALID>"), *priority);
+ fr_table_str_by_value(unlang_action_table, ua, "<INVALID>"), *priority);
fr_assert(*priority >= -1);
fr_assert(*priority <= MOD_PRIORITY_MAX);
- switch (action) {
+ switch (ua) {
/*
* The request is now defunct, and we should not
* continue processing it.
DUMP_STACK;
return UNLANG_FRAME_ACTION_YIELD;
+ /*
+ * This action is intended to be returned by library
+ * functions. It reduces the boilerplate.
+ */
+ case UNLANG_ACTION_FAIL:
+ *result = RLM_MODULE_FAIL;
+ FALL_THROUGH;
+
/*
* Instruction finished execution,
* check to see what we need to do next, and update
* Execute the next instruction in this frame
*/
case UNLANG_ACTION_EXECUTE_NEXT:
- if ((action == UNLANG_ACTION_EXECUTE_NEXT) && unlang_ops[instruction->type].debug_braces) {
+ if ((ua == UNLANG_ACTION_EXECUTE_NEXT) && unlang_ops[instruction->type].debug_braces) {
REXDENT();
RDEBUG2("}");
}
return 0;
}
-/** Allocate a subrequest to run through a virtual server at some point in the future
- *
- * @param[in] parent to hang sub request off of.
- * @param[in] namespace the child will operate in.
- * @return
- * - A new child request.
- * - NULL on failure.
- */
-request_t *unlang_module_subrequest_alloc(request_t *parent, fr_dict_t const *namespace)
-{
- return unlang_io_subrequest_alloc(parent, namespace, UNLANG_NORMAL_CHILD);
-}
-
/** Push a pre-compiled xlat and resumption state onto the stack for evaluation
*
* In order to use the async unlang processor the calling module needs to establish
case UNLANG_ACTION_UNWIND:
break;
+ case UNLANG_ACTION_FAIL:
+ *p_result = RLM_MODULE_FAIL;
+ break;
+
case UNLANG_ACTION_EXECUTE_NEXT: /* Not valid */
fr_assert(0);
*p_result = RLM_MODULE_FAIL;
case UNLANG_ACTION_UNWIND:
break;
+ case UNLANG_ACTION_FAIL:
+ *p_result = RLM_MODULE_FAIL;
+ break;
+
case UNLANG_ACTION_EXECUTE_NEXT:
fr_assert(0);
*p_result = RLM_MODULE_FAIL;
module_instance_t *module_instance, module_method_t method, bool top_frame)
CC_HINT(warn_unused_result);
-request_t *unlang_module_subrequest_alloc(request_t *parent, fr_dict_t const *namespace);
-
unlang_action_t unlang_module_yield_to_subrequest(rlm_rcode_t *p_result, request_t *child,
unlang_module_resume_t resume,
unlang_module_signal_t signal,
} else {
unlang_stack_frame_t *child_frame;
- if (unlang_interpret_push_function(child,
- NULL,
- unlang_parallel_child_done,
- unlang_parallel_child_signal,
- UNLANG_TOP_FRAME,
- &state->children[i]) < 0) goto error;
+ if (unlang_function_push(child,
+ NULL,
+ unlang_parallel_child_done,
+ unlang_parallel_child_signal,
+ UNLANG_TOP_FRAME,
+ &state->children[i]) < 0) goto error;
child_frame = frame_current(child);
return_point_set(child_frame); /* Don't unwind this frame */
* If there's a no destination tmpl, we're done.
*/
if (!child->reply) {
- unlang_subrequest_free(&child);
+ unlang_subrequest_detach_and_free(&child);
return UNLANG_ACTION_CALCULATE_RESULT;
}
}
}
- unlang_subrequest_free(&child);
+ unlang_subrequest_detach_and_free(&child);
return UNLANG_ACTION_CALCULATE_RESULT;
}
*
* @param[in] child to free.
*/
-void unlang_subrequest_free(request_t **child)
+void unlang_subrequest_detach_and_free(request_t **child)
{
request_detach(*child);
talloc_free(*child);
*child = NULL;
}
+/** Allocate a subrequest to run through a virtual server at some point in the future
+ *
+ * @param[in] parent to hang sub request off of.
+ * @param[in] namespace the child will operate in.
+ * @return
+ * - A new child request.
+ * - NULL on failure.
+ */
+request_t *unlang_subrequest_alloc(request_t *parent, fr_dict_t const *namespace)
+{
+ return unlang_io_subrequest_alloc(parent, namespace, UNLANG_NORMAL_CHILD);
+}
+
/** Initialise subrequest ops
*
*/
int unique_int; //!< Session unique int identifier.
} unlang_subrequest_session_t;
+request_t *unlang_subrequest_alloc(request_t *parent, fr_dict_t const *namespace);
+
+void unlang_subrequest_detach_and_free(request_t **child);
+
int unlang_subrequest_lifetime_set(request_t *request);
int unlang_subrequest_child_push(rlm_rcode_t *out, request_t *child,
/*
* Push a resume frame into the child
*/
- if (unlang_interpret_push_function(child, NULL,
+ if (unlang_function_push(child, NULL,
unlang_subrequest_child_done,
unlang_subrequest_child_signal, UNLANG_TOP_FRAME, state) < 0) return -1;
* The child *MUST* have been allocated with unlang_io_subrequest_alloc, or something
* that calls it.
*
- * After the child is no longer required it *MUST* be freed with #unlang_subrequest_free.
+ * After the child is no longer required it *MUST* be freed with #unlang_subrequest_detach_and_free.
* It's not enough to free it with talloc_free.
*
* This function should be called _before_ pushing any additional frames onto the child's
return (unlang_group_t *)subrequest;
}
-void unlang_subrequest_free(request_t **child);
-
int unlang_subrequest_detach_child(request_t *request);
#ifdef __cplusplus
/*
* Allocate a new subrequest
*/
- MEM(eap_session->subrequest = unlang_module_subrequest_alloc(request,
+ MEM(eap_session->subrequest = unlang_subrequest_alloc(request,
method->submodule->namespace ?
*(method->submodule->namespace) :
request->dict));
/*
* Catch the interpreter on the way back up the stack
*/
- if (unlang_interpret_push_function(request, NULL, eap_tls_virtual_server_result, NULL,
- UNLANG_SUB_FRAME, eap_session) < 0) {
- RETURN_MODULE_FAIL;
- }
+ if (unlang_function_push(request, NULL, eap_tls_virtual_server_result, NULL,
+ UNLANG_SUB_FRAME, eap_session) < 0) RETURN_MODULE_FAIL;
/*
* Push unlang instructions for the virtual server section