]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
have "try" cache the "catch" instructions
authorAlan T. DeKok <aland@freeradius.org>
Sun, 27 Jul 2025 15:38:38 +0000 (11:38 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 27 Jul 2025 23:28:58 +0000 (19:28 -0400)
and rearrange the code so that we don't have to skip to the next
catch, it just happens automatically in the interpreter

src/lib/unlang/catch.c
src/lib/unlang/catch_priv.h [deleted file]
src/lib/unlang/compile.c
src/lib/unlang/try.c
src/lib/unlang/try_priv.h
src/tests/keywords/try

index f8fdd465ecd1962038897b3e5505b00453461cdb..102526e3eb46b52eceb1b4a8f59854bc875931ef 100644 (file)
@@ -26,75 +26,15 @@ RCSID("$Id$")
 
 #include <freeradius-devel/server/rcode.h>
 #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, "<invalid>"));
-                       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, "<invalid>"));
-       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 (file)
index 425a688..0000000
+++ /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
index 4c2758ee3bbbdcd2c8911660f798eb00a064f288..793ac4433edb0d66044e8b089f51975b5b6cd15f 100644 (file)
@@ -37,7 +37,6 @@ RCSID("$Id$")
 
 #include <freeradius-devel/unlang/xlat_priv.h>
 
-#include "catch_priv.h"
 #include "call_priv.h"
 #include "caller_priv.h"
 #include "condition_priv.h"
index 83165a2ff6bbd6f255532b77af0a7a5e69b697d9..4f47f0a45a56f97cfc781e0023a85718c7fdb480 100644 (file)
@@ -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, "<invalid>"));
+
+       /*
+        *      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,
index adb8c5f2a01f1c864d5a4f07a95b0f7ffd3249d9..82841dcbde8256b1c0217d8125f9133a396131cc 100644 (file)
@@ -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
index dc47fb650685b44baeb8262a57588965a2d5ef00..b6e9b9b3c4794696478b8f74c486d99431cbe4e4 100644 (file)
@@ -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
        }