From: Alan T. DeKok Date: Sun, 27 Jul 2025 15:38:38 +0000 (-0400) Subject: have "try" cache the "catch" instructions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d3ad924a4c82f84b2273b0433b0cde6d249ff75e;p=thirdparty%2Ffreeradius-server.git have "try" cache the "catch" instructions and rearrange the code so that we don't have to skip to the next catch, it just happens automatically in the interpreter --- diff --git a/src/lib/unlang/catch.c b/src/lib/unlang/catch.c index f8fdd465ec..102526e3eb 100644 --- a/src/lib/unlang/catch.c +++ b/src/lib/unlang/catch.c @@ -26,75 +26,15 @@ RCSID("$Id$") #include #include "unlang_priv.h" -#include "catch_priv.h" +#include "try_priv.h" -static unlang_action_t catch_skip_to_next(UNUSED unlang_result_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame) +static unlang_action_t unlang_catch(UNUSED unlang_result_t *p_result, request_t *request, UNUSED unlang_stack_frame_t *frame) { - unlang_t const *unlang = frame->instruction; - - fr_assert(frame->instruction->type == UNLANG_TYPE_CATCH); - - 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, NULL); -} - -static unlang_action_t unlang_catch(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) -{ -#ifndef NDEBUG - unlang_catch_t const *c = unlang_generic_to_catch(frame->instruction); - - fr_assert(!c->catching[p_result->rcode]); -#endif - - /* - * Skip over any "catch" statementa after this one. - */ - frame_repeat(frame, catch_skip_to_next); - return unlang_interpret_push_children(NULL, request, RLM_MODULE_NOT_SET, UNLANG_NEXT_SIBLING); } -/** Skip ahead to a particular "catch" instruction. - * - */ -unlang_action_t unlang_interpret_skip_to_catch(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) -{ - unlang_t const *unlang; - rlm_rcode_t rcode = unlang_interpret_rcode(request); - - fr_assert(frame->instruction->type == UNLANG_TYPE_TRY); - - for (unlang = unlang_list_next(frame->instruction->list, frame->instruction); - unlang != NULL; - unlang = unlang_list_next(unlang->list, unlang)) { - unlang_catch_t const *c; - - if (unlang->type != UNLANG_TYPE_CATCH) { - RDEBUG3("No catch section for %s", - fr_table_str_by_value(mod_rcode_table, rcode, "")); - return frame_set_next(frame, unlang); - } - - if (rcode >= RLM_MODULE_NUMCODES) continue; - - c = unlang_generic_to_catch(unlang); - if (c->catching[rcode]) { - return frame_set_next(frame, unlang); - } - } - - RDEBUG3("No catch section for %s", - fr_table_str_by_value(mod_rcode_table, rcode, "")); - return frame_set_next(frame, NULL); -} - -static int catch_argv(CONF_SECTION *cs, unlang_catch_t *ca, char const *name) +static int catch_argv(CONF_SECTION *cs, unlang_try_t *gext, char const *name, unlang_t *c) { int rcode; @@ -104,45 +44,41 @@ static int catch_argv(CONF_SECTION *cs, unlang_catch_t *ca, char const *name) return -1; } - if (ca->catching[rcode]) { + if (gext->catch[rcode]) { cf_log_err(cs, "Duplicate rcode '%s'.", name); return -1; } - ca->catching[rcode] = true; + gext->catch[rcode] = c; return 0; } static unlang_t *unlang_compile_catch(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci) { - CONF_SECTION *cs = cf_item_to_section(ci); - unlang_group_t *g; - unlang_t *c; - unlang_catch_t *ca; - CONF_ITEM *prev; - char const *name; - - prev = cf_item_prev(cf_parent(ci), ci); - while (prev && cf_item_is_data(prev)) prev = cf_item_prev(cf_parent(ci), prev); - - if (!prev || !cf_item_is_section(prev)) { - fail: + CONF_SECTION *cs = cf_item_to_section(ci); + unlang_group_t *g; + unlang_t *c; + unlang_try_t *gext; + char const *name; + + g = unlang_generic_to_group(parent); + fr_assert(g != NULL); + + /* + * "catch" is NOT inserted into the normal child list. + * It's an exception, and is run only if the "try" says + * to run it. + */ + c = unlang_list_tail(&g->children); + if (!c || c->type != UNLANG_TYPE_TRY) { cf_log_err(cs, "Found 'catch' section with no previous 'try'"); cf_log_err(ci, DOC_KEYWORD_REF(catch)); return NULL; } - name = cf_section_name1(cf_item_to_section(prev)); - fr_assert(name != NULL); - - if ((strcmp(name, "try") != 0) && (strcmp(name, "catch") != 0)) { - /* - * The previous thing has to be a section. And it has to - * be either a "try" or a "catch". - */ - goto fail; - } + gext = talloc_get_type_abort(c, unlang_try_t); + fr_assert(gext != NULL); g = unlang_group_allocate(parent, cs, UNLANG_TYPE_CATCH); if (!g) return NULL; @@ -154,28 +90,27 @@ static unlang_t *unlang_compile_catch(unlang_t *parent, unlang_compile_ctx_t *un */ c->debug_name = c->name = talloc_typed_asprintf(c, "%s %s", cf_section_name1(cs), cf_section_name2(cs)); - ca = unlang_group_to_catch(g); if (!cf_section_name2(cs)) { /* * No arg2: catch errors */ - ca->catching[RLM_MODULE_REJECT] = true; - ca->catching[RLM_MODULE_FAIL] = true; - ca->catching[RLM_MODULE_INVALID] = true; - ca->catching[RLM_MODULE_DISALLOW] = true; + gext->catch[RLM_MODULE_REJECT] = c; + gext->catch[RLM_MODULE_FAIL] = c; + gext->catch[RLM_MODULE_INVALID] = c; + gext->catch[RLM_MODULE_DISALLOW] = c; } else { int i; name = cf_section_name2(cs); - if (catch_argv(cs, ca, name) < 0) { + if (catch_argv(cs, gext, name, c) < 0) { talloc_free(c); return NULL; } for (i = 0; (name = cf_section_argv(cs, i)) != NULL; i++) { - if (catch_argv(cs, ca, name) < 0) { + if (catch_argv(cs, gext, name, c) < 0) { talloc_free(c); return NULL; } @@ -183,9 +118,23 @@ static unlang_t *unlang_compile_catch(unlang_t *parent, unlang_compile_ctx_t *un } /* - * @todo - Else parse and limit the things we catch + * Compile our children. + */ + if (!unlang_compile_children(g, unlang_ctx)) { + return NULL; + } + + /* + * The "catch" section isn't in the parent list. It's just associated with "try". + */ + (void) talloc_steal(gext, c); + c->parent = &gext->group.self; + c->list = NULL; + + /* + * Don't insert it unto the normal list of children. */ - return unlang_compile_children(g, unlang_ctx); + return UNLANG_IGNORE; } void unlang_catch_init(void) @@ -198,7 +147,7 @@ void unlang_catch_init(void) .compile = unlang_compile_catch, .interpret = unlang_catch, - .unlang_size = sizeof(unlang_catch_t), - .unlang_name = "unlang_catch_t", + .unlang_size = sizeof(unlang_group_t), + .unlang_name = "unlang_group_t", }); } diff --git a/src/lib/unlang/catch_priv.h b/src/lib/unlang/catch_priv.h deleted file mode 100644 index 425a688697..0000000000 --- a/src/lib/unlang/catch_priv.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -/* - * 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. - */ - -/** - * $Id$ - * - * @file unlang/catch_priv.h - * @brief Declarations for the "catch" keyword - * - * @copyright 2006-2019 The FreeRADIUS server project - */ -#include "unlang_priv.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - unlang_group_t group; - bool catching[RLM_MODULE_NUMCODES]; -} unlang_catch_t; - -unlang_action_t unlang_interpret_skip_to_catch(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame); - -/** Cast a group structure to the transaction keyword extension - * - */ -static inline unlang_catch_t *unlang_group_to_catch(unlang_group_t *g) -{ - return talloc_get_type_abort(g, unlang_catch_t); -} - -/** Cast a generic structure to the catch keyword extension - * - */ -static inline unlang_catch_t const *unlang_generic_to_catch(unlang_t const *g) -{ - return talloc_get_type_abort_const(g, unlang_catch_t); -} - -#ifdef __cplusplus -} -#endif diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index 4c2758ee3b..793ac4433e 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -37,7 +37,6 @@ RCSID("$Id$") #include -#include "catch_priv.h" #include "call_priv.h" #include "caller_priv.h" #include "condition_priv.h" diff --git a/src/lib/unlang/try.c b/src/lib/unlang/try.c index 83165a2ff6..4f47f0a45a 100644 --- a/src/lib/unlang/try.c +++ b/src/lib/unlang/try.c @@ -26,7 +26,35 @@ RCSID("$Id$") #include "unlang_priv.h" #include "try_priv.h" -#include "catch_priv.h" + +static unlang_action_t skip_to_catch(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) +{ + rlm_rcode_t rcode = unlang_interpret_rcode(request); + unlang_try_t const *gext = unlang_generic_to_try(frame->instruction); + + fr_assert(frame->instruction->type == UNLANG_TYPE_TRY); + + /* + * Push the one "catch" section that we want to run. Once it's done, it will pop, return to us, + * and we will continue with the next sibling. + */ + if (gext->catch[rcode]) { + if (unlang_interpret_push(NULL, request, gext->catch[rcode], + FRAME_CONF(RLM_MODULE_NOT_SET, UNLANG_SUB_FRAME), false) < 0) { + return UNLANG_ACTION_STOP_PROCESSING; + } + + return UNLANG_ACTION_PUSHED_CHILD; + } + + RDEBUG3("No catch section for %s", + fr_table_str_by_value(mod_rcode_table, rcode, "")); + + /* + * Go to the next sibling, which MUST NOT be a "catch". + */ + return UNLANG_ACTION_CALCULATE_RESULT; +} static unlang_action_t unlang_try(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) { @@ -35,17 +63,17 @@ static unlang_action_t unlang_try(UNUSED unlang_result_t *p_result, request_t *r * * All of the magic is done in the compile phase. */ - frame_repeat(frame, unlang_interpret_skip_to_catch); + frame_repeat(frame, skip_to_catch); return unlang_interpret_push_children(NULL, request, RLM_MODULE_NOT_SET, UNLANG_NEXT_SIBLING); } static unlang_t *unlang_compile_try(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci) { - CONF_SECTION *cs = cf_item_to_section(ci); - unlang_group_t *g; - unlang_t *c; - CONF_ITEM *next; + CONF_SECTION *cs = cf_item_to_section(ci); + unlang_group_t *g; + unlang_t *c; + CONF_SECTION *parent_cs, *next; /* * The transaction is empty, ignore it. @@ -62,11 +90,11 @@ static unlang_t *unlang_compile_try(unlang_t *parent, unlang_compile_ctx_t *unla goto print_url; } - next = cf_item_next(cf_parent(cs), ci); - while (next && cf_item_is_data(next)) next = cf_item_next(cf_parent(cs), next); + parent_cs = cf_item_to_section(cf_parent(cs)); + next = cf_section_next(parent_cs, cs); - if (!next || !cf_item_is_section(next) || - (strcmp(cf_section_name1(cf_item_to_section(next)), "catch") != 0)) { + if (!next || + (strcmp(cf_section_name1(next), "catch") != 0)) { cf_log_err(cs, "'try' sections must be followed by a 'catch'"); goto print_url; } @@ -86,7 +114,7 @@ void unlang_try_init(void) unlang_register(&(unlang_op_t){ .name = "try", .type = UNLANG_TYPE_TRY, - .flag = UNLANG_OP_FLAG_DEBUG_BRACES, + .flag = UNLANG_OP_FLAG_DEBUG_BRACES | UNLANG_OP_FLAG_RCODE_SET, .compile = unlang_compile_try, .interpret = unlang_try, diff --git a/src/lib/unlang/try_priv.h b/src/lib/unlang/try_priv.h index adb8c5f2a0..82841dcbde 100644 --- a/src/lib/unlang/try_priv.h +++ b/src/lib/unlang/try_priv.h @@ -31,8 +31,18 @@ extern "C" { typedef struct { unlang_group_t group; + + unlang_t *catch[RLM_MODULE_NUMCODES]; } unlang_try_t; +/** Cast a generic structure to the try keyword extension + * + */ +static inline unlang_try_t const *unlang_generic_to_try(unlang_t const *g) +{ + return talloc_get_type_abort_const(g, unlang_try_t); +} + #ifdef __cplusplus } #endif diff --git a/src/tests/keywords/try b/src/tests/keywords/try index dc47fb6506..b6e9b9b3c4 100644 --- a/src/tests/keywords/try +++ b/src/tests/keywords/try @@ -14,7 +14,11 @@ try { catch disallow { test_fail } -catch ok reject fail { +catch ok reject { + test_fail +} + +catch fail { if foo != "hello" { test_fail }