From 861d1111c3dc4953cf7c1510cbbc017c496128e0 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 6 Mar 2026 14:32:51 +0100 Subject: [PATCH] MINOR: actions: store the location of keywords registered via initcalls A bit similar to what was done for sample fetch functions and converters, we now store with each action keyword the location of the initcall when they're registered this way. Since there are many functions only calling a LIST_APPEND() (one per ruleset), we now implement a dedicated function to store the context in all keywords before doing the append. However that's not sufficient, because keywords are not mandatory for actions, so we cannot safely rely on rule->kw. Thus we then set the exec_ctx per rule when they are all scanned in check_action_rules(), based on the keyword if it exists, otherwise we make a context from the action_ptr function if it is set (it should). Finally at all call points we now check rule->exec_ctx. --- include/haproxy/action-t.h | 3 +++ include/haproxy/action.h | 1 + src/action.c | 28 +++++++++++++++++++++++++++- src/http_ana.c | 6 ++++-- src/http_rules.c | 6 +++--- src/quic_rules.c | 5 +++-- src/stream.c | 2 +- src/tcp_rules.c | 22 ++++++++++++++-------- src/tcpcheck.c | 5 +++-- 9 files changed, 59 insertions(+), 19 deletions(-) diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index 96b0243ae..c10360e2b 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -206,6 +206,7 @@ struct act_rule { void *p[4]; } act; /* generic pointers to be used by custom actions */ } arg; /* arguments used by some actions */ + struct thread_exec_ctx exec_ctx; /* execution context */ struct { char *file; /* file name where the rule appears (or NULL) */ int line; /* line number where the rule appears */ @@ -217,7 +218,9 @@ struct action_kw { enum act_parse_ret (*parse)(const char **args, int *cur_arg, struct proxy *px, struct act_rule *rule, char **err); int flags; + /* 4 bytes here */ void *private; + struct thread_exec_ctx exec_ctx; /* execution context */ }; struct action_kw_list { diff --git a/include/haproxy/action.h b/include/haproxy/action.h index 3841df990..1dad89ff8 100644 --- a/include/haproxy/action.h +++ b/include/haproxy/action.h @@ -35,6 +35,7 @@ int act_resolution_cb(struct resolv_requester *requester, struct dns_counters *c int act_resolution_error_cb(struct resolv_requester *requester, int error_code); const char *action_suggest(const char *word, const struct list *keywords, const char **extra); void free_act_rule(struct act_rule *rule); +void act_add_list(struct list *head, struct action_kw_list *kw_list); static inline struct action_kw *action_lookup(struct list *keywords, const char *kw) { diff --git a/src/action.c b/src/action.c index 8a1486d10..9c5153b1c 100644 --- a/src/action.c +++ b/src/action.c @@ -25,7 +25,8 @@ /* Check an action ruleset validity. It returns the number of error encountered - * and err_code is updated if a warning is emitted. + * and err_code is updated if a warning is emitted. It also takes this + * opportunity for filling the execution context based on available info. */ int check_action_rules(struct list *rules, struct proxy *px, int *err_code) { @@ -40,6 +41,13 @@ int check_action_rules(struct list *rules, struct proxy *px, int *err_code) } *err_code |= warnif_tcp_http_cond(px, rule->cond); ha_free(&errmsg); + + if (!rule->exec_ctx.type) { + if (rule->kw && rule->kw->exec_ctx.type) + rule->exec_ctx = rule->kw->exec_ctx; + else if (rule->action_ptr) + rule->exec_ctx = EXEC_CTX_MAKE(TH_EX_CTX_FUNC, rule->action_ptr); + } } return err; @@ -378,3 +386,21 @@ void dump_act_rules(const struct list *rules, const char *pfx) (akwn->flags & KWF_MATCH_PREFIX) ? "*" : ""); } } + +/* adds the keyword list kw_list to the head */ +void act_add_list(struct list *head, struct action_kw_list *kw_list) +{ + int i; + + for (i = 0; kw_list->kw[i].kw != NULL; i++) { + /* store declaration file/line if known */ + if (kw_list->kw[i].exec_ctx.type) + continue; + + if (caller_initcall) { + kw_list->kw[i].exec_ctx.type = TH_EX_CTX_INITCALL; + kw_list->kw[i].exec_ctx.initcall = caller_initcall; + } + } + LIST_APPEND(head, &kw_list->list); +} diff --git a/src/http_ana.c b/src/http_ana.c index 7cd6529f1..d878d806d 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2881,7 +2881,8 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis s->waiting_entity.ptr = NULL; } - switch (rule->action_ptr(rule, px, sess, s, act_opts)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, px, sess, s, act_opts))) { case ACT_RET_CONT: break; case ACT_RET_STOP: @@ -3073,7 +3074,8 @@ resume_execution: s->waiting_entity.ptr = NULL; } - switch (rule->action_ptr(rule, px, sess, s, act_opts)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, px, sess, s, act_opts))) { case ACT_RET_CONT: break; case ACT_RET_STOP: diff --git a/src/http_rules.c b/src/http_rules.c index 8a3f9ec17..ee4543083 100644 --- a/src/http_rules.c +++ b/src/http_rules.c @@ -52,17 +52,17 @@ struct action_kw_list http_after_res_keywords = { void http_req_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&http_req_keywords.list, &kw_list->list); + act_add_list(&http_req_keywords.list, kw_list); } void http_res_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&http_res_keywords.list, &kw_list->list); + act_add_list(&http_res_keywords.list, kw_list); } void http_after_res_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&http_after_res_keywords.list, &kw_list->list); + act_add_list(&http_after_res_keywords.list, kw_list); } /* diff --git a/src/quic_rules.c b/src/quic_rules.c index 050548a8e..0da0aadf6 100644 --- a/src/quic_rules.c +++ b/src/quic_rules.c @@ -37,7 +37,8 @@ int quic_init_exec_rules(struct listener *li, struct quic_dgram *dgram) continue; if (rule->action_ptr) { - switch (rule->action_ptr(rule, px, &rule_sess, NULL, 0)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, px, &rule_sess, NULL, 0))) { case ACT_RET_CONT: break; case ACT_RET_DONE: @@ -142,7 +143,7 @@ struct action_kw_list quic_init_actions_list = { void quic_init_actions_register(struct action_kw_list *kw_list) { - LIST_APPEND(&quic_init_actions_list.list, &kw_list->list); + act_add_list(&quic_init_actions_list.list, kw_list); } /* Return the struct quic-initial action associated to a keyword. */ diff --git a/src/stream.c b/src/stream.c index 68aff8c26..2382c4a36 100644 --- a/src/stream.c +++ b/src/stream.c @@ -3380,7 +3380,7 @@ static enum act_parse_ret stream_parse_use_service(const char **args, int *cur_a void service_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&service_keywords, &kw_list->list); + act_add_list(&service_keywords, kw_list); } struct action_kw *service_find(const char *kw) diff --git a/src/tcp_rules.c b/src/tcp_rules.c index 785fcf85b..63ccc9610 100644 --- a/src/tcp_rules.c +++ b/src/tcp_rules.c @@ -45,22 +45,22 @@ struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords); */ void tcp_req_conn_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&tcp_req_conn_keywords, &kw_list->list); + act_add_list(&tcp_req_conn_keywords, kw_list); } void tcp_req_sess_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&tcp_req_sess_keywords, &kw_list->list); + act_add_list(&tcp_req_sess_keywords, kw_list); } void tcp_req_cont_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&tcp_req_cont_keywords, &kw_list->list); + act_add_list(&tcp_req_cont_keywords, kw_list); } void tcp_res_cont_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&tcp_res_cont_keywords, &kw_list->list); + act_add_list(&tcp_res_cont_keywords, kw_list); } /* @@ -187,7 +187,8 @@ int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit) resume_execution: /* Always call the action function if defined */ if (rule->action_ptr) { - switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, s->be, s->sess, s, act_opts))) { case ACT_RET_CONT: break; case ACT_RET_STOP: @@ -405,7 +406,8 @@ resume_execution: /* Always call the action function if defined */ if (rule->action_ptr) { - switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, s->be, s->sess, s, act_opts))) { case ACT_RET_CONT: break; case ACT_RET_STOP: @@ -570,7 +572,9 @@ int tcp_exec_l4_rules(struct session *sess) /* Always call the action function if defined */ if (rule->action_ptr) { - switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, sess->fe, sess, NULL, + ACT_OPT_FINAL | ACT_OPT_FIRST))) { case ACT_RET_YIELD: /* yield is not allowed at this point. If this return code is * used it is a bug, so I prefer to abort the process. @@ -659,7 +663,9 @@ int tcp_exec_l5_rules(struct session *sess) /* Always call the action function if defined */ if (rule->action_ptr) { - switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) { + switch (EXEC_CTX_WITH_RET(rule->exec_ctx, + rule->action_ptr(rule, sess->fe, sess, NULL, + ACT_OPT_FINAL | ACT_OPT_FIRST))) { case ACT_RET_YIELD: /* yield is not allowed at this point. If this return code is * used it is a bug, so I prefer to abort the process. diff --git a/src/tcpcheck.c b/src/tcpcheck.c index 86c798e73..362673ddd 100644 --- a/src/tcpcheck.c +++ b/src/tcpcheck.c @@ -2417,7 +2417,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_action_kw(struct check *check, struct tcpch enum act_return act_ret; act_rule =rule->action_kw.rule; - act_ret = act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0); + act_ret = EXEC_CTX_WITH_RET(act_rule->exec_ctx, + act_rule->action_ptr(act_rule, check->proxy, check->sess, NULL, 0)); if (act_ret != ACT_RET_CONT) { chunk_printf(&trash, "TCPCHK ACTION unexpected result at step %d\n", tcpcheck_get_step_id(check, rule)); @@ -2636,7 +2637,7 @@ int tcpcheck_main(struct check *check) void tcp_check_keywords_register(struct action_kw_list *kw_list) { - LIST_APPEND(&tcp_check_keywords.list, &kw_list->list); + act_add_list(&tcp_check_keywords.list, kw_list); } /**************************************************************************/