* DIE DIE DIE DIE DIE DIE DIE DIE DIE
* DIE DIE DIE.
*/
- if (!g->children) {
+ if (unlang_list_empty(&g->children)) {
frame_repeat(frame, unlang_call_resume);
} else {
frame_repeat(frame, unlang_call_children);
.attr_packet_type = attr_packet_type
};
+ unlang_group_type_init(&c->group.self, NULL, UNLANG_TYPE_CALL);
+
/*
* Push a new call frame onto the stack
*/
unlang_group_t *g = unlang_generic_to_group(frame->instruction);
unlang_caller_t *gext = unlang_group_to_caller(g);
- fr_assert(g->num_children > 0); /* otherwise the compilation is broken */
+ fr_assert(!unlang_list_empty(&g->children)); /* otherwise the compilation is broken */
/*
* No parent, or the dictionaries don't match. Ignore it.
talloc_steal(gext, dict_ref);
}
- if (!g->num_children) {
+ if (unlang_list_empty(&g->children)) {
talloc_free(c);
return UNLANG_IGNORE;
}
static unlang_action_t catch_skip_to_next(UNUSED unlang_result_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame)
{
- unlang_t *unlang;
+ unlang_t const *unlang = frame->instruction;
fr_assert(frame->instruction->type == UNLANG_TYPE_CATCH);
- for (unlang = frame->instruction->next;
- unlang != NULL;
- unlang = unlang->next) {
- if (unlang->type == UNLANG_TYPE_CATCH) continue;
-
- break;
+ while ((unlang = unlang_list_next(unlang->list, unlang)) != NULL) {
+ if (unlang->type != UNLANG_TYPE_CATCH) {
+ return frame_set_next(frame, unlang);
+ }
}
- return frame_set_next(frame, unlang);
+ return frame_set_next(frame, NULL);
}
static unlang_action_t unlang_catch(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
*/
unlang_action_t unlang_interpret_skip_to_catch(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
{
- unlang_t *unlang;
+ unlang_t const *unlang;
rlm_rcode_t rcode = unlang_interpret_rcode(request);
fr_assert(frame->instruction->type == UNLANG_TYPE_TRY);
- /*
- * 'try' at the end of a block without 'catch' should have been caught by the compiler.
- */
- fr_assert(frame->instruction->next);
-
- for (unlang = frame->instruction->next;
+ for (unlang = unlang_list_next(frame->instruction->list, frame->instruction);
unlang != NULL;
- unlang = unlang->next) {
+ unlang = unlang_list_next(unlang->list, unlang)) {
unlang_catch_t const *c;
if (unlang->type != UNLANG_TYPE_CATCH) {
- not_caught:
RDEBUG3("No catch section for %s",
fr_table_str_by_value(mod_rcode_table, rcode, "<invalid>"));
return frame_set_next(frame, unlang);
if (rcode >= RLM_MODULE_NUMCODES) continue;
c = unlang_generic_to_catch(unlang);
- if (c->catching[rcode]) break;
+ if (c->catching[rcode]) {
+ return frame_set_next(frame, unlang);
+ }
}
- if (!unlang) goto not_caught;
- return frame_set_next(frame, unlang);
+ RDEBUG3("No catch section for %s",
+ fr_table_str_by_value(mod_rcode_table, rcode, "<invalid>"));
+ return frame_set_next(frame, NULL);
}
static int catch_argv(CONF_SECTION *cs, unlang_catch_t *ca, char const *name)
static void unlang_dump(unlang_t *instruction, int depth)
{
- unlang_t *c;
unlang_group_t *g;
map_t *map;
char buffer[1024];
- for (c = instruction; c != NULL; c = c->next) {
+ unlang_list_foreach(instruction->list, c) {
switch (c->type) {
case UNLANG_TYPE_NULL:
case UNLANG_TYPE_CHILD_REQUEST:
case UNLANG_TYPE_TRANSACTION:
case UNLANG_TYPE_TRY:
case UNLANG_TYPE_CATCH: /* @todo - print out things we catch, too */
- g = unlang_generic_to_group(c);
DEBUG("%.*s%s {", depth, unlang_spaces, c->debug_name);
- unlang_dump(g->children, depth + 1);
+ unlang_dump(c, depth + 1);
DEBUG("%.*s}", depth, unlang_spaces);
break;
op->pool_headers, op->pool_len);
if (!g) return NULL;
- g->children = NULL;
- g->tail = &g->children;
g->cs = cs;
c = unlang_group_to_generic(g);
- c->parent = parent;
- c->type = type;
c->ci = CF_TO_ITEM(cs);
+ unlang_group_type_init(c, parent, type);
+
return g;
}
if (!edit) return NULL;
c = out = unlang_edit_to_generic(edit);
- c->parent = parent;
- c->next = NULL;
+ unlang_type_init(c, parent, UNLANG_TYPE_EDIT);
c->name = cf_section_name1(cs);
c->debug_name = c->name;
- c->type = UNLANG_TYPE_EDIT;
c->ci = CF_TO_ITEM(cs);
map_list_init(&edit->maps);
if (!edit) return NULL;
c = out = unlang_edit_to_generic(edit);
- c->parent = parent;
- c->next = NULL;
+ unlang_type_init(c, parent, UNLANG_TYPE_EDIT);
c->name = cf_pair_attr(cp);
c->debug_name = c->name;
- c->type = UNLANG_TYPE_EDIT;
c->ci = CF_TO_ITEM(cp);
map_list_init(&edit->maps);
*/
fr_assert(g == talloc_parent(single));
fr_assert(single->parent == unlang_group_to_generic(g));
- fr_assert(!single->next);
-
- *g->tail = single;
- g->tail = &single->next;
- g->num_children++;
+ unlang_list_insert_tail(&g->children, single);
/*
* If it's not possible to execute statement
MEM(ut = talloc_zero(parent, unlang_tmpl_t));
c = unlang_tmpl_to_generic(ut);
- c->parent = parent;
- c->next = NULL;
+ unlang_type_init(c, parent, UNLANG_TYPE_TMPL);
c->name = p;
c->debug_name = c->name;
- c->type = UNLANG_TYPE_TMPL;
c->ci = CF_TO_ITEM(cp);
RULES_VERIFY(unlang_ctx->rules);
}
c = unlang_module_to_generic(m);
- c->parent = parent;
- c->next = NULL;
-
+ unlang_type_init(c, parent, UNLANG_TYPE_MODULE);
c->name = talloc_typed_strdup(c, name);
c->debug_name = c->name;
- c->type = UNLANG_TYPE_MODULE;
c->ci = ci;
/*
fr_log(log, L_DBG, file, line, "count=%" PRIu64 " cpu_time=%" PRId64 " yielded_time=%" PRId64 ,
t->use_count, fr_time_delta_unwrap(t->tracking.running_total), fr_time_delta_unwrap(t->tracking.waiting_total));
- if (g->children) {
- unlang_t *child;
-
- for (child = g->children; child != NULL; child = child->next) {
+ if (!unlang_list_empty(&g->children)) {
+ unlang_list_foreach(&g->children, child) {
unlang_perf_dump(log, child, depth + 1);
}
}
unlang_frame_state_cond_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_cond_t);
fr_value_box_t *box = fr_value_box_list_head(&state->out);
bool value;
+ unlang_t const *unlang;
/*
* Something in the conditional evaluation failed.
* Tell the main interpreter to skip over the else /
* elsif blocks, as this "if" condition was taken.
*/
- while (frame->next &&
- ((frame->next->type == UNLANG_TYPE_ELSE) ||
- (frame->next->type == UNLANG_TYPE_ELSIF))) {
- frame->next = frame->next->next;
+ for (unlang = frame->next;
+ unlang != NULL;
+ unlang = unlang_list_next(unlang->list, unlang)) {
+ if ((unlang->type == UNLANG_TYPE_ELSE) ||
+ (unlang->type == UNLANG_TYPE_ELSIF)) {
+ continue;
+ }
+
+ break;
}
+ /*
+ * Do NOT call frame_set_next(), as that will clean up the current frame.
+ */
+ frame->next = unlang;
+
/*
* We took the "if". Go recurse into its' children.
*/
*edit = (unlang_edit_t) {
.self = edit_instruction,
};
+
+ unlang_type_init(&edit->self, NULL, UNLANG_TYPE_EDIT);
map_list_init(&edit->maps);
/*
frame->instruction = instruction;
- if (do_next_sibling) {
+ if (do_next_sibling && instruction->list) {
fr_assert(instruction != NULL);
- frame->next = instruction->next;
+ frame->next = unlang_list_next(instruction->list, instruction);
}
/* else frame->next MUST be NULL */
* top-level 'recv Access-Request' etc. Which can exist,
* and can be empty.
*/
- if (!g->children) {
+ if (unlang_list_empty(&g->children)) {
RDEBUG2("... ignoring empty subsection ...");
return UNLANG_ACTION_EXECUTE_NEXT;
}
- if (unlang_interpret_push(p_result, request, g->children,
+ if (unlang_interpret_push(p_result, request, unlang_list_head(&g->children),
FRAME_CONF(default_rcode, UNLANG_SUB_FRAME), do_next_sibling) < 0) {
return UNLANG_ACTION_STOP_PROCESSING;
}
* We finished the previous child, and it failed. Go to the next one. If we fall off of the
* end, loop around to the next one.
*/
- redundant->child = redundant->child->next;
- if (!redundant->child) redundant->child = g->children;
+ redundant->child = unlang_list_next(&g->children, redundant->child);
+ if (!redundant->child) redundant->child = unlang_list_head(&g->children);
/*
* We looped back to the start. Return whatever results we had from the last child.
/*
* Start at the first child, and then continue from there.
*/
- redundant->start = g->children;
+ redundant->start = unlang_list_head(&g->children);
frame->process = unlang_load_balance_next;
return unlang_load_balance_next(p_result, request, frame);
unlang_frame_state_redundant_t *redundant;
unlang_group_t *g = unlang_generic_to_group(frame->instruction);
unlang_load_balance_t *gext = NULL;
- unlang_t *child;
uint32_t count = 0;
#ifdef STATIC_ANALYZER
- if (!g || !g->num_children) return UNLANG_ACTION_STOP_PROCESSING;
+ if (!g || unlang_list_empty(&g->children)) return UNLANG_ACTION_STOP_PROCESSING;
#else
fr_assert(g != NULL);
- fr_assert(g->num_children);
+ fr_assert(!unlang_list_empty(&g->children));
#endif
gext = unlang_group_to_load_balance(g);
fr_assert(fr_type_is_leaf(vp->vp_type));
- start = fr_value_box_hash(&vp->data) % g->num_children;
+ start = fr_value_box_hash(&vp->data) % unlang_list_num_elements(&g->children);
} else {
uint8_t *octets = NULL;
hash = fr_hash(octets, slen);
- start = hash % g->num_children;
+ start = hash % unlang_list_num_elements(&g->children);
}
RDEBUG3("load-balance starting at child %d", (int) start);
count = 0;
- for (child = redundant->start = g->children; child != NULL; child = child->next) {
- count++;
+ unlang_list_foreach(&g->children, child) {
if (count == start) {
redundant->start = child;
break;
}
+
+ count++;
}
+ fr_assert(redundant->start != NULL);
} else {
randomly_choose:
- count = 0;
+ count = 1;
/*
* Choose a child at random.
* for this load-balance section. So for now,
* just pick a random child.
*/
- for (child = redundant->start = g->children; child != NULL; child = child->next) {
- count++;
+ unlang_list_foreach(&g->children, child) {
if ((count * (fr_rand() & 0xffffff)) < (uint32_t) 0x1000000) {
redundant->start = child;
}
+ count++;
}
+
+ fr_assert(redundant->start != NULL);
+
}
fr_assert(redundant->start != NULL);
*/
MEM(frame->state = update_state = talloc_zero_pooled_object(request->stack, unlang_frame_state_update_t,
(sizeof(map_t) +
- (sizeof(tmpl_t) * 2) + 128),
- g->num_children)); /* 128 is for string buffers */
+ (sizeof(tmpl_t) * 2) + 128), /* 128 is for string buffers */
+ unlang_list_num_elements(&g->children)));
fr_dcursor_init(&update_state->maps, &gext->map.head);
fr_value_box_list_init(&update_state->lhs_result);
}
}
};
+ unlang_type_init(&mc->self, NULL, UNLANG_TYPE_MODULE);
/*
* Push a new module frame onto the stack
unlang_group_t *g;
unlang_parallel_t *gext;
unlang_parallel_state_t *state;
- int i;
+ int i;
+ size_t num_children;
g = unlang_generic_to_group(frame->instruction);
- if (!g->num_children) RETURN_UNLANG_NOOP;
+
+ num_children = unlang_list_num_elements(&g->children);
+ if (num_children == 0) RETURN_UNLANG_NOOP;
gext = unlang_group_to_parallel(g);
*/
MEM(frame->state = state = _talloc_zero_pooled_object(request,
sizeof(unlang_parallel_state_t) +
- (sizeof(state->children[0]) * g->num_children),
+ (sizeof(state->children[0]) * num_children),
"unlang_parallel_state_t",
- g->num_children,
+ num_children,
(talloc_array_length(request->name) * 2)));
if (!state) {
return UNLANG_ACTION_FAIL;
state->result = UNLANG_RESULT_NOT_SET;
state->detach = gext->detach;
state->clone = gext->clone;
- state->num_children = g->num_children;
+ state->num_children = unlang_list_num_elements(&g->children);
/*
* Initialize all of the children.
*/
- for (i = 0, instruction = g->children; instruction != NULL; i++, instruction = instruction->next) {
+ for (i = 0, instruction = unlang_list_head(&g->children);
+ instruction != NULL;
+ i++, instruction = unlang_list_next(&g->children, instruction)) {
request_t *child;
unlang_result_t *child_result;
if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
/*
- * Parallel sections can create empty children, if the
- * admin demands it. Otherwise, the principle of least
- * surprise is to copy the whole request, reply, and
- * config items.
+ * Parallel sections can create empty child requests, if
+ * the admin demands it. Otherwise, the principle of
+ * least surprise is to copy the whole request, reply,
+ * and config items.
*/
name2 = cf_section_name2(cs);
if (name2) {
* Initialize the state
*/
g = unlang_generic_to_group(frame->instruction);
- if (!g->num_children) RETURN_UNLANG_NOOP;
+ if (unlang_list_empty(&g->children)) RETURN_UNLANG_NOOP;
gext = unlang_group_to_subrequest(g);
child = unlang_io_subrequest_alloc(request, gext->dict, UNLANG_DETACHABLE);
* Push the first instruction the child's
* going to run.
*/
- if (unlang_interpret_push(NULL, child, g->children,
+ if (unlang_interpret_push(NULL, child, unlang_list_head(&g->children),
FRAME_CONF(RLM_MODULE_NOT_SET, UNLANG_SUB_FRAME),
UNLANG_NEXT_SIBLING) < 0) goto fail;
tmpl_t *vpt = NULL, *src_vpt = NULL, *dst_vpt = NULL;
+ if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
+
/*
* subrequest { ... }
*
{
unlang_group_t *g = unlang_generic_to_group(frame->instruction);
- if (!g->children) RETURN_UNLANG_NOOP;
+ if (unlang_list_empty(&g->children)) RETURN_UNLANG_NOOP;
return unlang_group(p_result, request, frame);
}
goto error;
}
- *g->tail = single;
- g->tail = &single->next;
- g->num_children++;
+ unlang_list_insert_tail(&g->children, single);
}
return c;
.self = p_result ? tmpl_instruction_fail : tmpl_instruction_return,
.tmpl = tmpl
};
+ unlang_type_init(&ut->self, NULL, UNLANG_TYPE_TMPL);
/*
* Push a new tmpl frame onto the stack
#include <freeradius-devel/server/modpriv.h>
#include <freeradius-devel/server/time_tracking.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/util/dlist.h>
#include <freeradius-devel/unlang/base.h>
#include <freeradius-devel/unlang/map.h>
#include <freeradius-devel/io/listen.h>
typedef struct unlang_s unlang_t;
typedef struct unlang_stack_frame_s unlang_stack_frame_t;
+FR_DLIST_TYPES(unlang_list)
+FR_DLIST_TYPEDEFS(unlang_list, unlang_list_t, unlang_entry_t)
+
/** 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
*/
struct unlang_s {
unlang_t *parent; //!< Previous node.
- unlang_t *next; //!< Next node (executed on #UNLANG_ACTION_EXECUTE_NEXT et al).
+ unlang_list_t *list; //!< so we have fewer run-time dereferences
+ unlang_entry_t entry; //!< next / prev entries
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.
unlang_mod_actions_t actions; //!< Priorities, etc. for the various return codes.
};
+FR_DLIST_FUNCS(unlang_list, unlang_t, entry)
+#define unlang_list_foreach(_list_head, _iter) fr_dlist_foreach(unlang_list_dlist_head(_list_head), unlang_t, _iter)
+
typedef struct {
fr_dict_t *dict; //!< our dictionary
fr_dict_attr_t const *root; //!< the root of our dictionary
*/
typedef struct {
unlang_t self;
- unlang_t *children; //!< Children beneath this group. The body of an if
- //!< section for example.
- unlang_t **tail; //!< pointer to the tail which gets updated
+
CONF_SECTION *cs;
- int num_children;
+ unlang_list_t children;
unlang_variable_t *variables; //!< rarely used, so we don't usually need it
} unlang_group_t;
if (!frame->instruction) return; /* No siblings, need to pop instead */
- frame->next = frame->instruction->next;
+ frame->next = unlang_list_next(frame->instruction->list, frame->instruction);
/*
* We _may_ want to take a new frame->p_result value in future but
frame->process = process;
}
-static inline unlang_action_t frame_set_next(unlang_stack_frame_t *frame, unlang_t *unlang)
+static inline unlang_action_t frame_set_next(unlang_stack_frame_t *frame, unlang_t const *unlang)
{
/*
* We're skipping the remaining siblings, stop the
return UNCONST(unlang_t *, p);
}
+static inline CC_HINT(always_inline) unlang_list_t *unlang_list(unlang_t *unlang)
+{
+ return &unlang_generic_to_group(unlang)->children;
+}
+
+static inline CC_HINT(always_inline) void unlang_type_init(unlang_t *unlang, unlang_t *parent, unlang_type_t type)
+{
+ unlang->type = type;
+ unlang->parent = parent;
+ if (parent) unlang->list = unlang_list(parent);
+ unlang_list_entry_init(unlang);
+}
+
+static inline CC_HINT(always_inline) void unlang_group_type_init(unlang_t *unlang, unlang_t *parent, unlang_type_t type)
+{
+ unlang_type_init(unlang, parent, type);
+ unlang_list_init(&((unlang_group_t *) unlang)->children);
+}
+
static inline unlang_tmpl_t *unlang_generic_to_tmpl(unlang_t const *p)
{
fr_assert(p->type == UNLANG_TYPE_TMPL);