]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: vars: store variable names for runtime access
authorHyeonggeun Oh <hyeonggeun.oh@plaintexting.com>
Tue, 13 Jan 2026 18:20:27 +0000 (03:20 +0900)
committerWilly Tarreau <w@1wt.eu>
Wed, 21 Jan 2026 09:44:19 +0000 (10:44 +0100)
Currently, variable names are only used during parsing and are not
stored at runtime. This makes it impossible to iterate through
variables and retrieve their names.

This patch adds infrastructure to store variable names:
- Add 'name' and 'name_len' fields to var_desc structure
- Add 'name' field to var structure
- Add VDF_NAME_ALLOCATED flag to track memory ownership
- Store names in vars_fill_desc(), var_set(), vars_check_arg(),
  and parse_store()
- Free names in var_clear() and release_store_rule()
- Add ARGT_VAR handling in release_sample_arg() to free the
  allocated name when the flag is set

This prepares the ground for implementing dump_all_vars() in the
next commit.

Tested with:
  - ASAN-enabled build on Linux (TARGET=linux-glibc USE_OPENSSL=1
    ARCH_FLAGS="-g -fsanitize=address")
  - Regression tests: reg-tests/sample_fetches/vars.vtc
  - Regression tests: reg-tests/startup/default_rules.vtc

include/haproxy/vars-t.h
src/sample.c
src/vars.c

index 105506977c0e1e6ea040a9a159bbc46495bf308b..24f629743584fcfd9883cf739a9a28acc36840a3 100644 (file)
@@ -57,12 +57,15 @@ struct vars {
 };
 
 #define VDF_PARENT_CTX       0x00000001   // Set if the variable is related to the parent stream
+#define VDF_NAME_ALLOCATED   0x00000002   // Set if name was allocated and must be freed
 
 /* This struct describes a variable as found in an arg_data */
 struct var_desc {
        uint64_t name_hash;
        enum vars_scope scope;
        uint flags; /*VDF_* */
+       const char *name; /* variable name (not owned) */
+       size_t name_len; /* variable name length */
 };
 
 struct var {
@@ -70,6 +73,7 @@ struct var {
        uint64_t name_hash;        /* XXH3() of the variable's name, indexed by <name_node> */
        uint flags;       // VF_*
        /* 32-bit hole here */
+       char *name; /* variable name (allocated) */
        struct sample_data data; /* data storage. */
 };
 
index 31fe56fd84ef14a8c83c1d4fc129daa351df5cf0..a35d997f767920c7dc4c6ea67e3d0ce486a99e36 100644 (file)
@@ -1741,6 +1741,10 @@ static void release_sample_arg(struct arg *p)
                        regex_free(p->data.reg);
                        p->data.reg = NULL;
                }
+               else if (p->type == ARGT_VAR) {
+                       if (p->data.var.flags & VDF_NAME_ALLOCATED)
+                               ha_free((char **)&p->data.var.name);
+               }
                p++;
        }
 
index 1755bf8ad1e09c0c7541c7a86821762ffc73fedd..7589a7a13c710953ceab49a296b46429f3c0c77e 100644 (file)
@@ -178,6 +178,8 @@ unsigned int var_clear(struct vars *vars, struct var *var, int force)
 {
        unsigned int size = 0;
 
+       ha_free(&var->name);
+
        if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
                ha_free(&var->data.u.str.area);
                size += var->data.u.str.data;
@@ -322,6 +324,8 @@ static int vars_fill_desc(const char *name, int len, struct var_desc *desc, char
        }
 
        desc->name_hash = XXH3(name, len, var_name_hash_seed);
+       desc->name = name;
+       desc->name_len = len;
        return 1;
 }
 
@@ -431,6 +435,16 @@ int var_set(const struct var_desc *desc, struct sample *smp, uint flags)
                        goto unlock;
                var->name_hash = desc->name_hash;
                var->flags = flags & VF_PERMANENT;
+
+               /* Save variable name */
+               var->name = NULL;
+               if (desc->name && desc->name_len > 0) {
+                       var->name = my_strndup(desc->name, desc->name_len);
+                       if (!var->name) {
+                               pool_free(var_pool, var);
+                               goto unlock;
+                       }
+               }
                var->data.type = SMP_T_ANY;
                cebu64_item_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], name_node, name_hash, var);
        }
@@ -623,6 +637,7 @@ int vars_check_arg(struct arg *arg, char **err)
 {
        struct sample empty_smp = { };
        struct var_desc desc;
+       char *saved_name = NULL;
 
        /* Check arg type. */
        if (arg->type != ARGT_STR) {
@@ -634,15 +649,30 @@ int vars_check_arg(struct arg *arg, char **err)
        if (!vars_fill_desc(arg->data.str.area, arg->data.str.data, &desc, err))
                return 0;
 
-       if (desc.scope == SCOPE_PROC && !var_set(&desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
+       /* Save variable name before destroying the chunk */
+       if (desc.name && desc.name_len > 0) {
+               saved_name = my_strndup(desc.name, desc.name_len);
+               if (!saved_name) {
+                       memprintf(err, "out of memory");
+                       return 0;
+               }
+
+               desc.name = saved_name;
+               desc.flags |= VDF_NAME_ALLOCATED;
+       }
+
+       if (desc.scope == SCOPE_PROC && !var_set(&desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT)) {
+               if (desc.flags & VDF_NAME_ALLOCATED)
+                       ha_free(&saved_name);
                return 0;
+       }
 
        /* properly destroy the chunk */
        chunk_destroy(&arg->data.str);
 
        /* Use the global variable name pointer. */
        arg->type = ARGT_VAR;
-       arg->data.var = desc;
+       arg->data.var = desc;  /* desc.name already points to saved_name */
        return 1;
 }
 
@@ -868,6 +898,10 @@ static void release_store_rule(struct act_rule *rule)
        lf_expr_deinit(&rule->arg.vars.fmt);
 
        release_sample_expr(rule->arg.vars.expr);
+
+       /* Free variable name if allocated */
+       if (rule->arg.vars.desc.flags & VDF_NAME_ALLOCATED)
+               ha_free((char **)&rule->arg.vars.desc.name);
 }
 
 /* This two function checks the variable name and replace the
@@ -919,6 +953,7 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy
        struct ist condition = IST_NULL;
        struct ist var = IST_NULL;
        struct ist varname_ist = IST_NULL;
+       char *saved_name = NULL;
 
        if (strncmp(var_name, "set-var-fmt", 11) == 0) {
                var_name += 11;
@@ -973,6 +1008,17 @@ static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy
        if (!vars_fill_desc(var_name, var_len, &rule->arg.vars.desc, err))
                return ACT_RET_PRS_ERR;
 
+       /* Save variable name for runtime use */
+       if (rule->arg.vars.desc.name && rule->arg.vars.desc.name_len > 0) {
+               saved_name = my_strndup(rule->arg.vars.desc.name, rule->arg.vars.desc.name_len);
+               if (!saved_name) {
+                       memprintf(err, "out of memory");
+                       return ACT_RET_PRS_ERR;
+               }
+               rule->arg.vars.desc.name = saved_name;
+               rule->arg.vars.desc.flags |= VDF_NAME_ALLOCATED;
+       }
+
        if (rule->arg.vars.desc.scope == SCOPE_PROC &&
            !var_set(&rule->arg.vars.desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
                return 0;